<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Hao's Learning Log]]></title><description><![CDATA[I write about software development, productivity, career tips, personal development and more!]]></description><link>https://blog.hao.dev</link><generator>GatsbyJS</generator><lastBuildDate>Fri, 08 Nov 2024 18:36:32 GMT</lastBuildDate><item><title><![CDATA[Reflecting on AI Dev Tools in 2024]]></title><description><![CDATA[<p>The last couple of years have been a magical time for AI fans, with new tools, AI models, services, or apps popping up every other week. As someone who was very skeptical at the start, I thought it would be interesting to reflect on how AI has changed my workflow and review what I’m currently using.</p>
<p>A quick disclaimer: I’m not a fan of the term “AI” since nothing we have right now deserves to be called “artificial intelligence.” These are just machine learning data models. But for the sake of consistency, I will refer to them as “AI” since it is now the accepted way to refer to machine learning.</p>]]></description><link>https://blog.hao.dev/reflecting-on-ai-dev-tools-in-2024</link><guid isPermaLink="false">https://blog.hao.dev/reflecting-on-ai-dev-tools-in-2024</guid><pubDate>Sat, 09 Nov 2024 06:17:00 GMT</pubDate><content:encoded>&lt;p&gt;The last couple of years have been a magical time for AI fans, with new tools, AI models, services, or apps popping up every other week. As someone who was very skeptical at the start, I thought it would be interesting to reflect on how AI has changed my workflow and review what I’m currently using.&lt;/p&gt;
&lt;p&gt;A quick disclaimer: I’m not a fan of the term “AI” since nothing we have right now deserves to be called “artificial intelligence.” These are just machine learning data models. But for the sake of consistency, I will refer to them as “AI” since it is now the accepted way to refer to machine learning.&lt;/p&gt;&lt;h2&gt;Coding&lt;/h2&gt;
&lt;p&gt;I recently moved off GitHub’s Copilot and adopted Cursor with the Claude data model, and I really enjoy the new workflow.&lt;/p&gt;
&lt;p&gt;With Copilot, my usage was mostly limited to the code auto-complete feature, which helped my coding a little. I would switch between turning it on and off because it was slow to respond to my changes and often disrupted my workflow.&lt;/p&gt;
&lt;p&gt;Cursor has been a big enough improvement that I decided to pay the monthly subscription. The difference isn’t world-shattering, but it is significant enough for someone like me—who hates subscriptions—to bite the bullet. For one, the Claude model is much better than Copilot, which is based on ChatGPT. And secondly, Cursor’s AI editor-level features are much more intuitive to use than VS Code.&lt;/p&gt;
&lt;p&gt;After using Cursor + Claude for two weeks, I noticed myself reaching out for AI assistance more frequently, and my productivity has gone up as a result. I would even dare to say my developer experience has improved because boring, repetitive tasks now feel less of a drag.&lt;/p&gt;
&lt;h3&gt;Problem Solving&lt;/h3&gt;
&lt;p&gt;When I’m stuck on an issue now, I open up the side panel, add the right context (tag files, etc.), and see what Claude comes up with. There have definitely been times when it was able to give me a solution right away, although that is rare. Even if it doesn’t give me the answer, I find that the back-and-forth conversation makes the debugging process more bearable.&lt;/p&gt;
&lt;p&gt;Maybe I’m a bit old-school, but I still prefer to tackle the problem myself and only reach for AI’s help when I feel like I’m not making any progress. I want to maintain and practice my problem-solving abilities—both to keep myself employed and because this is why I do this for a living.&lt;/p&gt;
&lt;p&gt;Just a side note: I still Google things most of the time. Especially for simple questions, Google can often give me an answer in a few seconds (search + click a couple of links) faster than the generative AI can. Maybe I just hate waiting for the text to be generated and want to feel like I’m doing something, but I find using Google is still less taxing when I’m in the zone.&lt;/p&gt;
&lt;h3&gt;Rubber Ducking&lt;/h3&gt;
&lt;p&gt;As software engineers, we all know too well the feeling of being stuck on a problem for hours, if not days, only to have someone jump on a call and find the solution before we finish explaining the problem. This is known as rubber ducking or rubber duck debugging.&lt;/p&gt;
&lt;p&gt;It’s very common to get so deep into a problem that we lose sight of the bigger picture and forget to step back. This is why many people swear by pair programming. Personally, I prefer to pair-program intentionally; blindly pairing on all tasks is a waste of engineering time. It’s often frustrating both when I’m in the zone and the constant interruptions cause extra context switching, and when I need help but can’t find anyone to pair with. AI solves this problem.&lt;/p&gt;
&lt;p&gt;When using Claude to debug issues, it’s the perfect rubber duck. It has no context, so I have to explain the problem. And while explaining the problem, I get a chance to reset and look at the issue again more holistically.&lt;/p&gt;
&lt;h3&gt;Code Complete&lt;/h3&gt;
&lt;p&gt;This is probably what most people think of when AI coding comes up. I still use it, and it’s nice when it helps me finish writing a function or a line. It would be a lie to ignore the time saved here, but for me, this isn’t the most useful part, which is why I would turn off Copilot sometimes out of frustration. Personally, I find the ability to have a conversation about a problem, with the AI understanding the context, much more useful.&lt;/p&gt;
&lt;h2&gt;Writing&lt;/h2&gt;
&lt;p&gt;For software engineers, written communication is often part of the job, especially as they move up in seniority. This could be writing documentation for other engineers, communicating architecture or design changes to wider engineering communities, presenting technical analysis to non-technical stakeholders, or simply writing a good PR description. Whatever the scenario, my experience has been that the majority of developers lack strong writing skills (myself included). I’ve seen some beautifully written documents from senior engineers I’ve worked with in the past, but I wouldn’t expect that level of writing from everyone.&lt;/p&gt;
&lt;h3&gt;Polish And Refine Text&lt;/h3&gt;
&lt;p&gt;One thing I’ve noticed myself doing more over the last year or so is leaning on ChatGPT to polish what I’ve written. I’ll brain dump what I want to say, then paste it into ChatGPT and ask it to consolidate and reword it. The result is often better than what I had initially. It does have a tendency to reword things in a more formal or even academic tone, which then requires further refinement, or I’ll just manually update them myself if there isn’t much to change.&lt;/p&gt;
&lt;h3&gt;Generating Ideas&lt;/h3&gt;
&lt;p&gt;One of the things I struggle with in writing is getting started. When preparing an analysis document, for example, there might be several angles to tackle the topic. In this situation, I write down some key points I want to cover, then use ChatGPT to generate a skeleton document and see what points I might have missed. Once I have a list of points to cover, it becomes easier to structure and present them.&lt;/p&gt;
&lt;h3&gt;Summarising Text&lt;/h3&gt;
&lt;p&gt;I’ve started using ChatGPT to summarise text more recently. That said, I still prefer to read through the original text. It’s the same reason I read books instead of book summaries—I find a lot of valuable information is lost in the summarisation process. Maybe I’ll change my mind as AI models improve, but for now, I’ll keep this usage to a minimum.&lt;/p&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;There has been a lot of hype around AI in recent years—just look at all the money thrown at AI startups. Not surprisingly, there has also been fear around AI taking over jobs, especially software engineering jobs. I have no doubt that as AI models get better, the number of software engineering jobs will plateau and maybe even slowly decrease over time.&lt;/p&gt;
&lt;p&gt;As I mentioned at the start of the article, “AI” has no intelligence; these are just machine learning models. So no matter how good they become, they won’t directly “replace” a skilled engineer. Even if they could, in the short to medium term, the compute costs would be so high that companies might as well hire a person to do the job—although this could change as AI chips become more powerful. In the near future, AI tools will likely improve engineers’ efficiency, so fewer team members will be needed to accomplish the same amount of work. Junior engineers will suffer most during this transition, as we’re already seeing.&lt;/p&gt;
&lt;p&gt;I don’t have a crystal ball, so I am not sure which direction things will go in the future. But at least in the short term, I’d advise all engineers to start leveraging these AI tools and get comfortable adopting them into their daily workflows to improve efficiency. From my experience, I’d estimate that using AI tools like ChatGPT and Claude with Cursor has increased my work efficiency by more than 20%.&lt;/p&gt;
&lt;p&gt;I wrote an article called &lt;a href=&quot;/a-web-dev-engineer-s-review-learning-swift-using-chatgpt&quot;&gt;A web dev engineer&apos;s review: Learning Swift using ChatGPT&lt;/a&gt; back in April 2023. It might be an interesting read also.&lt;/p&gt;
&lt;p&gt;PS: I only used AI to fix some grammar mistakes in this article.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[GraphQL at scale: schema stitching v.s. schema federation]]></title><description><![CDATA[<p>I have been helping to lead a GraphQL team at a large organization. The goal of my team is to create a scalable GraphQL platform and increase its adoption within this company. When I joined the team, we'd already gone through several iterations and failed to onboard any consumer in the 1 - 2 year period. The approach at the time of my joining was to use schema stitching with an open-source framework called GraphQL Mesh. After experiencing numerous challenges and we are now shifting to Apollo Federation as an alternative, which as the name suggests federates the schema instead of stitching them. In this article, I will share the differences between these two approaches and walk through my experiences with them including why we making the switch, particularly in regards to scalability and stability.</p>]]></description><link>https://blog.hao.dev/graphql-at-scale-schema-stitching-v-s-schema-federation</link><guid isPermaLink="false">https://blog.hao.dev/graphql-at-scale-schema-stitching-v-s-schema-federation</guid><pubDate>Fri, 18 Aug 2023 16:20:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been helping to lead a GraphQL team at a large organization. The goal of my team is to create a scalable GraphQL platform and increase its adoption within this company. When I joined the team, we&apos;d already gone through several iterations and failed to onboard any consumer in the 1 - 2 year period. The approach at the time of my joining was to use schema stitching with an open-source framework called GraphQL Mesh. After experiencing numerous challenges and we are now shifting to Apollo Federation as an alternative, which as the name suggests federates the schema instead of stitching them. In this article, I will share the differences between these two approaches and walk through my experiences with them including why we making the switch, particularly in regards to scalability and stability.&lt;/p&gt;&lt;h2&gt;What is GraphQL schema and why is it important?&lt;/h2&gt;
&lt;p&gt;A GraphQL schema is a way of defining the types, queries, and mutations that can be used in a GraphQL service. If you are unfamiliar, the closest thing in the REST world is probably an API specification.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of what a GraphQL schema might look like:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;gql&quot;&gt;&lt;pre class=&quot;language-gql&quot;&gt;&lt;code class=&quot;language-gql&quot;&gt;type Query {
 user(id: ID!): User
 users: [User]
}

type User {
 id: ID!
 name: String
 email: String
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;One could say the GraphQL schema serves as the backbone of a GraphQL API since it defines the structure and shape of the data that can be queried or mutated. The schema act as the contract between the backend and the frontend, which can also be viewed as documentation. With tools such as &lt;a href=&quot;https://the-guild.dev/graphql/codegen&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;graphql-codegen&lt;/a&gt;, the consumer of a GraphQL service can generate boilerplate code and type definitions from the schema, saving engineers&apos; time and effort.&lt;/p&gt;
&lt;h2&gt;The single GraphQL service approach doesn&apos;t scale&lt;/h2&gt;
&lt;p&gt;The common implementation of a GraphQL service is to set it up as a layer in front of other data providers, such as API services or databases. All requests from clients go through the GraphQL service in the form of GraphQL operations (queries, mutations, and subscriptions). The GraphQL service then processes these requests and forwards them to the appropriate data providers. Once the data has been retrieved, the GraphQL service touches up the data and returns it to the client. The diagram below for an illustration of this process.&lt;/p&gt;
&lt;p&gt;In this setup, often teams follow a BFF (backend for frontend) approach, formatting and tweaking the schema to match the expectations of the consumer.&lt;/p&gt;
&lt;p&gt;Initially, teams will start by converting or adding a GraphQL service in front of one existing backend service. Then over time, as it proves to be beneficial, teams will start adding more backend services to the same GraphQL service, as shown in the diagram below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1uss4b79IITYVgsc9uorWP/c6ad965827955e15e96f7ef7c9697e02/vanilla-graphql-schema.png&quot; alt=&quot;vanilla-graphql-schema&quot;&gt;&lt;/p&gt;
&lt;p&gt;While this approach works well for small organizations with a limited number of data providers, it can become problematic in larger organizations.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Adding new data providers to the GraphQL service can be time-consuming and error-prone since the schema must be defined manually for each provider.&lt;/li&gt;
&lt;li&gt;Additionally, as the number of data providers increases, the GraphQL service can become a monorepo that slows down development due to its growing internal complexities. &lt;/li&gt;
&lt;li&gt;The GraphQL team can become a bottleneck, as all changes to data providers must be coordinated with changes to the GraphQL schema to ensure that the schema and data responses remain in sync.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the beginning, the biggest issue for our team was the very first point. There were already many APIs within the organisation which we could integrate with, but it took a long time to get them added to the earlier iterations of the GraphQL service. To workaround this problem, we looked toward adopting the schema stitching strategy that came with GraphQL Mesh. It allowed us to add new data providers more easily by automating the conversion of API specs to GraphQL schemas. GraphQL Mesh also helps to reduce the complexity of coordinating changes between the GraphQL schema and data providers with the help of a service called Hive (we&apos;ll touch on this later). &lt;/p&gt;
&lt;h2&gt;Schema stitching&lt;/h2&gt;
&lt;p&gt;Schema stitching in essence is the process of combining multiple GraphQL schemas into a single, unified schema that is presented to the consumer. This was a concept initially proposed by &lt;a href=&quot;https://www.apollographql.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&lt;/a&gt;, which they later abandoned and then was picked back up by &lt;a href=&quot;https://the-guild.dev/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The Guild&lt;/a&gt; who built GraphQL Mesh on top of it. The approach is great if a company is looking to migrate from traditional REST APIs to GraphQL, because with Mesh offers a plug-n-play integration flow, assuming of course each REST API came with API specs (e.g. Swagger, OpenAPI, RAML, etc).&lt;/p&gt;
&lt;p&gt;With Mesh, naturally, we adopted its sibling SaSS service Hive as well. Hive, an open-source tool also created by The Guild, is a schema registry with some handy features that synergy well with Mesh or any GraphQL service in general. Hive is a schema CDN, that hosts the latest schema and stores previous versions of the schema. This means it has a historical view of how the schema evolved over time but also offers the ability to track each iteration of the schema on each data source (e.g. REST API). Schema is like a contract, and it is very important for all parties to know when a contract has been broken so it can be quickly fixed. Hive supports the ability to perform schema checks and notify of any breaking changes. We also used the paid feature which allows us to send usage metrics to Hive and see relevant monitoring stats. Hive can then use these usage data to let us know if something is safe to be removed.&lt;/p&gt;
&lt;p&gt;After implementing schema stitching, the biggest win for us was how quickly and easily we could add data providers to the GraphQL service. At this point, our architecture looked something like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4mDzzvTMlk6bLxfMuXonkU/81d5f0b45bc5100735d5a239a9ea25dd/graphql-schema-stitching.png&quot; alt=&quot;graphql-schema-stitching&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now, schema stitching wasn&apos;t a flawless solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Schema stitching requires accurate and reliable API specifications, unfortunately, most of the REST APIs we were dealing with didn&apos;t have good or any API specs. This meant we had to either generate a schema from a raw API response (yes Mesh can do that) or we had to create and maintain the Swagger (or alternative) API specs manually. Both approaches were less than ideal and required massive maintenance effort.&lt;/li&gt;
&lt;li&gt;This is still a monolith solution, the issue mentioned in the previous section still applies here.&lt;/li&gt;
&lt;li&gt;We are still the bottleneck between consumer and data provider teams due to the lack of accurate API specs.&lt;/li&gt;
&lt;li&gt;When an issue occurs, we are the first to get notified but the issue often comes from the underlying data provider.&lt;/li&gt;
&lt;li&gt;A new problem was that Mesh packages are still in pre-stable phase and not following Semantic Versioning, so we often encountered bugs that led to a lot of headaches and slowed down development.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given the above challenges, we explored alternatives and decided to move to Apollo Federation.&lt;/p&gt;
&lt;h2&gt;Schema Federation&lt;/h2&gt;
&lt;p&gt;Schema federation refers to the process of combining multiple individual schemas into a single, federated schema. In a schema federation, each schema retains its structure and integrity, but a new, overarching schema is created that combines the various schemas into a single logical unit. This allows for seamless querying and integration of the different data sources, without the need to manually combine or transform the data.&lt;/p&gt;
&lt;p&gt;Using Apollo Federation, you can create a single GraphQL schema that is composed of multiple independent services called subgraphs. This allows you to build complex and powerful GraphQL APIs without having to create a monolithic schema that is managed by a single team or service. Instead, each service can manage its portion of the schema, and the federated schema is built automatically by combining the individual schemas.&lt;/p&gt;
&lt;p&gt;Just like Mesh has Hive as its schema registry, Apollo offers its SaSS service called Apollo GraphOS which offers all the features of Hive and more. You can read more about it in &lt;a href=&quot;https://www.apollographql.com/docs/graphos/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apollo&apos;s docs&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;How is it different to schema stitching?&lt;/h3&gt;
&lt;p&gt;In many ways, schema federation has many similarities to schema stitching. GraphQL schema federation and schema stitching are both techniques for combining multiple GraphQL schemas into a single, cohesive schema. However, there are some key differences between the two approaches.&lt;/p&gt;
&lt;p&gt;Schema stitching follows a central governance model, where each data provider doesn&apos;t need to know about each other. The GraphQL service that stitches all the schema is both the gateway and the brain that decide how schemas get stitched. Federation, on the other hand, has a router service but uses a decentralised governance model. It requires each data source to know about each other and define their relations between themselves.&lt;/p&gt;
&lt;p&gt;Schema federation is a built-in feature of the Apollo GraphQL platform, whereas schema stitching is a technique that can be used with any GraphQL implementation. This means that schema federation is designed to be easy to use and integrate with other Apollo tools, whereas schema stitching is a more general-purpose solution that may require more work to set up and use.&lt;/p&gt;
&lt;p&gt;Finally, schema federation uses a set of conventions and APIs to define how the individual schemas should be combined, whereas schema stitching typically involves writing custom code to manually merge the individual schemas. This means that schema federation can be easier to use and maintain, especially for large and complex GraphQL APIs, whereas schema stitching can be more flexible and allow for more customization.&lt;/p&gt;
&lt;h2&gt;How we use schema federation&lt;/h2&gt;
&lt;p&gt;With Federation, our new architecture now looks something like the following diagram.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5ri3DPvm95AxVtd8WimaQN/f07cfd21f918677db02a86c1368b0f57/graphql-schema-federation.png&quot; alt=&quot;graphql-schema-federation&quot;&gt;&lt;/p&gt;
&lt;p&gt;The idea is to have a separate team manage a single subgraph and its data provider(s). We then took this idea further by making these teams into domain-based backend teams, who are responsible for the domain subgraph and the data providers (in our case they are mostly REST APIs).&lt;/p&gt;
&lt;h2&gt;Which is the right one for you?&lt;/h2&gt;
&lt;p&gt;As mentioned already, we decided to ditch schema stitching and migrate over to federation. Partly because our current data providers (REST APIs) have room for improvement. As part of the migration, we are going to rewrite the current APIs into domain APIs with GraphQL subgraphs in front of them. The wider business decision to move to a domain-driven backend has very good synergy with Apollo Federation. Of course, these are probably unique to our situation and others may not be so lucky. So let&apos;s discuss some common pros and cons of each approach.&lt;/p&gt;
&lt;h3&gt;Benefits of schema federation&lt;/h3&gt;
&lt;p&gt;There are several other benefits to using schema federation over schema stitching when building a distributed GraphQL schema. Some of the key benefits include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Convenience&lt;/strong&gt;: Schema federation is a built-in feature of the Apollo GraphQL platform, so it is easy to use and integrate with other Apollo tools. This means you can quickly and easily create a federated schema without having to write a lot of custom code.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Maintainability&lt;/strong&gt;: Schema federation uses a set of conventions and APIs to define how the individual schemas should be combined, which makes it easier to maintain and update the federated schema over time. This can be especially helpful for large and complex GraphQL APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;: Schema federation is fully compatible with the Apollo Client and other popular GraphQL tools, so you can use it to build rich and powerful GraphQL-powered applications.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Performance&lt;/strong&gt;: Schema federation is designed to optimize performance and minimize network overhead, which can be important for large and complex GraphQL APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Stability&lt;/strong&gt;: From our testing Apollo software is more stable and mature compared to GraphQL Mesh which is the main if not only schema stitching tool out there.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Benefits of schema stitching&lt;/h3&gt;
&lt;p&gt;There are several benefits to using schema stitching over schema federation when building a distributed GraphQL schema. Some of the key benefits include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Flexibility&lt;/strong&gt;: With schema stitching, you have complete control over how the individual schemas are combined. This allows you to customize the schema stitching process to fit the specific needs of your application.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compatibility&lt;/strong&gt;: Schema stitching can be used with any GraphQL implementation, whereas schema federation is only available on the Apollo platform. This means that schema stitching may be a better option if you are using a different GraphQL implementation or if you want to use a mix of different GraphQL implementations in your schema.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Customization&lt;/strong&gt;: With schema stitching, you can write custom code to merge the individual schemas in any way you want. This allows you to add custom logic and behaviour to the schema stitching process, which can be useful for complex or specialized GraphQL APIs.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Open source&lt;/strong&gt;: all Mesh code is open source, so anyone could see and fork them as they see fit. The Guild even said they&apos;ll be making Hive (schema registry) open source as well.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Choosing the approach is important, but it is also worth thinking about the tools behind them as well. From our research, if you want to go with schema stitching then GraphQL Mesh is the only implementation right now, and similarly Apollo Federation is the only choice if you pick schema federation. While GraphQL Mesh gets additional points for being open source, it is still a few steps behind Apollo both in terms of scale and software maturity. For example, many of the Mesh packages are still not following Semantic Version at the time of writing, however, based on their &lt;a href=&quot;https://github.com/Urigo/graphql-mesh/issues/4287&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;roadmap&lt;/a&gt; this might not be the case for long. Until then, Apollo might be the better choice for teams wanting a stable platform to build their GraphQL services.&lt;/p&gt;
&lt;p&gt;As mentioned in the &quot;How is it different to schema stitching?&quot; section, the two approaches use different governance models. If you work in a large company, then I would suggest Federation because sooner or later you will reach a point where a single GraphQL team isn&apos;t enough to provide support, maintenance and development to meet the demand from all the consumers and data providers. Handling schema changes is not only time-consuming but requires careful coordination, in my team we found ourselves spending a decent amount of time preventing breaking changes and supporting new schema releases. In smaller companies or even medium-sized companies (several consumers and data providers instead of tens or even hundreds), schema stitching provides more flexibility and be more easily slotted into an existing REST backend architecture.&lt;/p&gt;
&lt;p&gt;Ultimately, the choice between schema stitching and schema federation will depend on the specific needs of your application, so it&apos;s important to evaluate both options and choose the one that best fits your requirements. This is the perfect situation to create PoCs and test out each approach before committing.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A web dev engineer's review: Learning Swift using ChatGPT]]></title><description><![CDATA[<p>ChatGPT was released back in November 2022, and it caused quite a stir online. Scaremongers have been saying it will change everything and AI is ready to take jobs from us. There are also sceptics which said it won't change anything. I was interested in how good ChatGPT was, so I decided to do an experiment and have been testing it out myself in the past couple of months. I wanted to see if it could help me to learn iOS mobile app development and how it faired compared to widely used methods such as "googling" and following text or video tutorials.</p>]]></description><link>https://blog.hao.dev/a-web-dev-engineer-s-review-learning-swift-using-chatgpt</link><guid isPermaLink="false">https://blog.hao.dev/a-web-dev-engineer-s-review-learning-swift-using-chatgpt</guid><pubDate>Sat, 08 Apr 2023 22:17:00 GMT</pubDate><content:encoded>&lt;p&gt;ChatGPT was released back in November 2022, and it caused quite a stir online. Scaremongers have been saying it will change everything and AI is ready to take jobs from us. There are also sceptics which said it won&apos;t change anything. I was interested in how good ChatGPT was, so I decided to do an experiment and have been testing it out myself in the past couple of months. I wanted to see if it could help me to learn iOS mobile app development and how it faired compared to widely used methods such as &quot;googling&quot; and following text or video tutorials.&lt;/p&gt;&lt;h2&gt;My background&lt;/h2&gt;
&lt;p&gt;I&apos;ve mainly been working in the web sphere during my career, which means I know next to nothing about native app development, especially in Apple&apos;s developer ecosystem. I briefly tested out Android and hybrid app development a few years back, but never checkout any of Apple&apos;s platforms. So I came into this experiment with zero knowledge about Xcode, the IDE for developing apps for any of Apple&apos;s platforms, or Swift, the programming language for developing apps. Thus this should be a perfect scenario to test out ChatGPT&apos;s limits.&lt;/p&gt;
&lt;h2&gt;Chosen project&lt;/h2&gt;
&lt;p&gt;What better way is there to learn about app development than creating an app? I love reading Chinese online novels, unfortunately, there isn&apos;t a good free reading app on iOS (Android users have a lot more choices). So I decided this would be my project to learn Swift. The app would essentially be an HTML scrapper that given some instructions (e.g. URLs and CSS selectors) can parse HTML into data which is then rendered on the screen. It&apos;ll need to be able to search multiple websites for books and pull information about a book such as name, author, table of contents URL, etc. Then using the table of contents URL it needs to parse a list of chapters, and finally, get the content from the currently reading chapter. Some other features will provide a better reading experience as well, such as the ability to change background colour, support multiple fonts, text-to-speech, data caching, etc.&lt;/p&gt;
&lt;p&gt;In my head, a lot of the functionalities boiled down to UI-specific stuff, and I was pretty confident I&apos;d be able to get them implemented without much issue. Others might be more difficult, especially learning how to parse HTML in Swift.&lt;/p&gt;
&lt;p&gt;In summary, I was very curious to learn more about both ChatGPT and iOS app development. With my motivation high, I cracked on at the beginning of Feb 2023.&lt;/p&gt;
&lt;h2&gt;ChatGPT only provide text-based answers&lt;/h2&gt;
&lt;p&gt;After I opened up Xcode, I was very lost. Although some of the interface made sense to me, most of it was alien as I&apos;ve always preferred text editors such as Sublime Text and VS Code instead of IDEs. This highlighted a massive limitation with ChatGPT, it doesn&apos;t do images. When learning something new, images can be worth more than a thousand words. Although it gave a lot of good background information about Xcode and some high-level features, it did not provide a good basic Xcode interface walkthrough. I had to result to watching a few YouTube videos showing me the basics of the different UI buttons and panels of Xcode.&lt;/p&gt;
&lt;p&gt;Maybe one-day ChatGPT 10 will be able to utilise more data formats but right now it is severely limited by the fact it can only communicate via text. This means for anyone who intends to use ChatGPT that relies heavily on visual aspects, might be disappointed with what ChatGPT can do.&lt;/p&gt;
&lt;h2&gt;ChatGPT is amazing at answering basic questions&lt;/h2&gt;
&lt;p&gt;I was massively impressed with how easily ChatGPT answered my basic and stupid questions early on regarding both Swift and SwiftUI. It not only generated code to further illustrate its points but was also able to contextually continue the conversation when I asked follow-up questions.&lt;/p&gt;
&lt;p&gt;There is an example where I asked it the difference between &lt;code class=&quot;language-text&quot;&gt;@State&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;@StateObject&lt;/code&gt; in Swift. Then without providing any context, I asked it to update its code example in the previous answer to showcase a simpler use case of &lt;code class=&quot;language-text&quot;&gt;@State&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;[INSERT IMAGES]&lt;/p&gt;
&lt;p&gt;With ChatGPT&apos;s help, I made a lot of progress at the beginning and quickly built up a basic book page screen that contained book cover images, author, word count, description of the book, etc. &lt;/p&gt;
&lt;p&gt;ChatGPT even helped me to correct my Xcode configuration when I had to request data from an HTTP URL instead of HTTPS. It turns out Apple blocks non-secure connections by default, and unless I add an exception in my &lt;code class=&quot;language-text&quot;&gt;Info.plist&lt;/code&gt; the app refused to make the connection.&lt;/p&gt;
&lt;h2&gt;ChatGPT struggles with complex code problems&lt;/h2&gt;
&lt;p&gt;Later, ChatGPT showed its limits when I hit a more complex problem. I wanted to take all the text from a single chapter (long string) and split it up into multiple chunks, where each chunk fits within the current available screen space. I needed this because I want to allow people to swipe horizontally and flip through the pages like many other reading apps.&lt;/p&gt;
&lt;p&gt;It turns out this is a non-trivial problem in Swift, and I struggled to get ChatGPT to give good answers. Some of the shortcomings of ChatGPT include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ChatGPT gave answers that did not satisfy my requirements. I tried to reword my request differently but it was never able to provide the code to split up the pages efficiently. It only managed to provide several half-complete and less optimal solutions.&lt;/li&gt;
&lt;li&gt;Some answers contained basic coding errors such as referring to APIs that did not exist in Swift. Interestingly, when I pasted in the Xcode error it was able to recognise its mistakes and apologised.&lt;/li&gt;
&lt;li&gt;ChatGPT started losing context between questions. This not only meant my follow-up question&apos;s changes were added to some new snippet of code or that changing the previous code introduced other errors. But most often, it would give me brand new code solutions out of nowhere when asked to change something in the previous code example.&lt;/li&gt;
&lt;li&gt;It was trained using old data (limited knowledge of the world and events after 2021) which means ChatGPT is not aware of the newer programming language changes. This can lead to some silly answers sometimes by returning deprecated code, etc.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This was a problem that I stuck on for days, ChatGPT and I went around in circles and it was frustrating. From this experience it is easy to see that ChatGPT is just a better Google search, it understands the question better and filters the results out with more context. But at the end of the date, it is just that, a better Google and it is not perfect.&lt;/p&gt;
&lt;h2&gt;ChatGPT can be very slow&lt;/h2&gt;
&lt;p&gt;There have been times when I simply gave up on asking ChatGPT questions because it was taking a very long time to respond. This is especially true when I used it during peak hours (free tier). However, its normal responding time can be long as well. Since it progressively generates its answers, where each word doesn&apos;t take long to generate but the time to generate them does not get any faster. This always means complex questions that require longer answers will take longer to generate than short answers. This is very different to Googling for results, some of the results&apos; relevance level might be low, but it&apos;ll return results quickly.&lt;/p&gt;
&lt;p&gt;When ChatGPT is working well, having it on the side feels like doing pair programming with another engineer, bouncing questions off it is a great way to learn. However, often the feedback loop was too long, which ended up taking me out of the &quot;zone&quot; and I found myself juggling more ideas in my head than usual as I try to break down questions into more manageable chunks for ChatGPT. Then holding on to the extra information in my head as I watch it slowly generate each word back to me.&lt;/p&gt;
&lt;h2&gt;I slowly stopped using ChatGPT&lt;/h2&gt;
&lt;p&gt;As I got better and more comfortable with Swift and Xcode, I used ChatGPT less and less. This should highlight the fact ChatGPT is just another tool in an engineer&apos;s toolbox and another source of information like Google results, YouTube videos, etc. &lt;/p&gt;
&lt;p&gt;At the start of the project, ChatGPT was contributing more to the project than I was. But as soon I started grasping the basics of Swift, I soon took over and only asked clarification questions such as &quot;Is it better to use @AppStorage or UserDefaults in Swift&quot; to confirm my understanding and avoid bad practices. ChatGPT&apos;s answers always made me smile by returning diplomatic answers that explain both concepts and conclude that &quot;it was dependent on my use case&quot;.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In summary, I have been very impressed with ChatGPT in the past couple of months. It has helped me work through some obscure issues, but at the same time when it came to complex programming problems, it struggled. So I had no choice but to architecting the app and take on the complex issues myself. &lt;/p&gt;
&lt;p&gt;I have to admit ChatGPT made coding more fun, especially when working on a personal project when there are no colleagues to chat with about approaches and problems. When I get stuck on a coding problem I often find it hard to find the motivation to continue. ChatGPT has made it less daunting when I hit a brick wall. It is like a patient and knowledgeable friend, guiding and pushing me towards my goal.&lt;/p&gt;
&lt;p&gt;Finally, in terms of the project, I&apos;m happy to share that after around two months the app is almost done. There are a few more features I want to implement, then I&apos;ll be asking ChatGPT&apos;s help to refactor the code as right now it is a mess!&lt;/p&gt;
&lt;h2&gt;Afterwords&lt;/h2&gt;
&lt;p&gt;The media love to add &quot;AI&quot; to anything that isn&apos;t achieved by a human, but we should be clear that ChatGPT is strictly not a true AI. It is still based on a Machine Learning data model, albeit the best one in the world so far. It has no consciousness and when it replies it is not writing out of its understanding of the world. This showed when I asked it the complex code problem explained above. ChatGPT is great at putting publically available knowledge together in a short and easy-to-consume result, aka generating information, but it cannot &quot;create&quot; anything that never existed on the internet. Again, my &quot;split this text into multiple pages&quot; problem was a good example of this.&lt;/p&gt;
&lt;p&gt;This also led me to question what would happen to the future ChatGPT versions. Before 2021, there was little to no &quot;AI&quot; generated content online (at least good content). But with the release of ChatGPT, I can imagine the internet will be plagued with generated content on so many blog posts (I&apos;m referring to people typing a title into ChatGPT and then publishing its response as a blog post). I am very curious how future &quot;AI&quot; will be trained, because if it starts to consume the content generated by other &quot;AI&quot; then it would go into a self-approving spiral. If a wrong piece of information appears frequently enough in its training data then it could be considered a fact, just look at the &lt;a href=&quot;https://www.theverge.com/2023/2/8/23590864/google-ai-chatbot-bard-mistake-error-exoplanet-demo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google’s AI chatbot Bard makes factual error in first demo&lt;/a&gt; example. But since the authors of ChatGPT have already altered how it responds to some questions, I&apos;m sure it&apos;s not an impossible task, may just require a lot of manual tinkering.&lt;/p&gt;
&lt;p&gt;Today I saw an article about how good ChatGPT was at medical diagnosis &lt;a href=&quot;https://www.yahoo.com/news/newest-version-chatgpt-passed-us-200346481.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;The newest version of ChatGPT passed the US medical licensing exam with flying colors — and diagnosed a 1 in 100,000 condition in seconds&lt;/a&gt;. Based on my coding experience with it so far, ChatGPT might be a good place to get some initial ideas about something, but I wouldn&apos;t trust it with my medical diagnosis yet. &lt;/p&gt;
&lt;p&gt;Note: this article was written with zero help from ChatGPT&lt;/p&gt;</content:encoded></item><item><title><![CDATA[When is GraphQL the right choice for your Project or Business?]]></title><description><![CDATA[<p>Building a GraphQL API requires a deep understanding of the technology and best practices. It offers developers a high level of control and freedom, which is why it is so easy to get started. However, it is also why people can design themselves into a corner if they are not careful. Security and performance implications are just typical issues of software development. GraphQL also requires that the schema is well-designed and easy to maintain; otherwise, the development team will run into a world of pain. Additionally, the development team must be trained and skilled in using GraphQL, as it requires a different way of thinking about data and APIs. In this article, let's discuss when it is a good choice to adopt GraphQL and who it is for.</p>]]></description><link>https://blog.hao.dev/when-is-graphql-the-right-choice-for-your-project-or-business</link><guid isPermaLink="false">https://blog.hao.dev/when-is-graphql-the-right-choice-for-your-project-or-business</guid><pubDate>Sun, 12 Feb 2023 22:21:00 GMT</pubDate><content:encoded>&lt;p&gt;Building a GraphQL API requires a deep understanding of the technology and best practices. It offers developers a high level of control and freedom, which is why it is so easy to get started. However, it is also why people can design themselves into a corner if they are not careful. Security and performance implications are just typical issues of software development. GraphQL also requires that the schema is well-designed and easy to maintain; otherwise, the development team will run into a world of pain. Additionally, the development team must be trained and skilled in using GraphQL, as it requires a different way of thinking about data and APIs. In this article, let&apos;s discuss when it is a good choice to adopt GraphQL and who it is for.&lt;/p&gt;&lt;h2&gt;What is GraphQL and why is it so good?&lt;/h2&gt;
&lt;p&gt;GraphQL provides a flexible and efficient way to query and manipulate data, as opposed to traditional REST APIs with a set structure and limitations in data retrieval. With GraphQL, the client has more control over the data it receives and can request exactly what it needs without over or under-fetching. This can result in faster and more efficient applications. Like REST, GraphQL can be used by any platform e.g. native mobile app, web, desktop app, etc.&lt;/p&gt;
&lt;p&gt;Here are some of the main selling points of GraphQL. Many of these addresses pain points seen in the REST world:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Flexibility: GraphQL allows the client to specify exactly what data it needs, reducing the amount of over- or under-fetching data compared to traditional REST APIs. It is easier to fetch the data required for a specific use case, leading to faster and more efficient applications.&lt;/li&gt;
&lt;li&gt;Performance: GraphQL can often result in fewer round trips to the server than REST APIs, as all the required data can be requested in a single request. It can lead to improved performance, especially in low-bandwidth or high-latency environments.&lt;/li&gt;
&lt;li&gt;Type System: GraphQL has a built-in type system that allows you to define the structure of your data, making it easier to validate user input and ensure that your API is always in a consistent state.&lt;/li&gt;
&lt;li&gt;Easy to use: GraphQL provides a simple yet powerful syntax for querying and manipulating data, making it easy for developers to start using it immediately.&lt;/li&gt;
&lt;li&gt;Better documentation: The GraphQL schema acts as documentation for your API, making it easier for other developers to understand how to use your API correctly.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;GraphQL isn&apos;t a REST replacement&lt;/h2&gt;
&lt;p&gt;There are many direct comparisons between GraphQL and REST. While it is good to have those discussions to understand the role of GraphQL in an organisation, the comparison is inaccurate at best. Because GraphQL isn&apos;t here to replace REST. Instead, it works best when acting as the interfacing layer that sits in front of REST.&lt;/p&gt;
&lt;p&gt;I have worked on projects where the architecture looks like the following diagram:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/b5kKIiGrfpJSzflSuZ4ph/21f68569ea33a5273fccecd02011c820/image.png&quot; alt=&quot;GraphQL connecting directly to DB&quot;&gt;&lt;/p&gt;
&lt;p&gt;We had GraphQL services that talked directly to SQL databases. It sounds great in theory but a nightmare in practice. We wrote SQL queries in GraphQL resolvers without help from the typical ORM database tools. It could have been a better dev experience and error-prone. The team did its best to refactor things and apply abstractions to reduce code logic duplications, but it didn&apos;t work. It was still hard to understand and messy. Not to mention testing anything is incredibly difficult.&lt;/p&gt;
&lt;p&gt;GraphQL offers the most value when it doesn&apos;t have to worry about the business logic and focuses on what it does best: distributing requests and aggregating results. Of course, it doesn&apos;t mean we shouldn&apos;t have business logic in GraphQL, but based on my experience, the less logic the better.&lt;/p&gt;
&lt;p&gt;I recommend that anyone thinking about adopting GraphQL or working on a new green field project consider the following architecture.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5eZwjVX0yXZl2qhE9jqsM2/4ea67de09b5507bc383610921e036073/image.png&quot; alt=&quot;GraphQL connecting to REST then to DB&quot;&gt;&lt;/p&gt;
&lt;p&gt;PS: there are SaSS services out there which can make generating some of the CRUD operations from the database easier. This might be attractive for small projects or startups. Still, it will be best for the business to leave these SaSS services and build out the REST layer internally for better control and support those customisations needed as part of the business growth journey.&lt;/p&gt;
&lt;h2&gt;GraphQL doesn&apos;t make getting your product out faster&lt;/h2&gt;
&lt;p&gt;GraphQL can be misconceiving when it comes to development time because it is perfect at some things and gives the wrong impression that it will always speed up your project.&lt;/p&gt;
&lt;p&gt;If you agree with what I talked about in the previous section, the first elephant in the room is that adopting GraphQL means building up one additional service and layer in your architecture. This translates to more development work, both in terms of writing the schema and resolvers. On the DevOps side, there are deployment pipelines, infrastructure setup, monitoring and alerting, security, etc. All of this can take more time than implementing the GraphQL service itself.&lt;/p&gt;
&lt;p&gt;As mentioned already, to build and use GraphQL effectively, the development team must have a deep understanding of GraphQL and consider security and performance implications. Wrong design decisions or choices could lead to more development time spent on rectifying these mistakes.&lt;/p&gt;
&lt;h2&gt;Who&apos;s best suited to adopt GraphQL?&lt;/h2&gt;
&lt;p&gt;GraphQL shines when it is used in medium or large companies with many APIs and client teams. Since GraphQL provides a single entry point for all the APIs client teams need. The added benefits of reduced over and under-fetching could improve client-side performance massively on low-end devices or customers with poor internet connections.&lt;/p&gt;
&lt;p&gt;Introducing GraphQL is easy, but the coordination needed to make GraphQL work well in an organisation is the real test. If not careful, it will cause more overhead and delays than it was intended to improve. Schema changes become a massive talking point because it impacts everyone, both the client teams consuming from GraphQL and the underlying API teams providing data to it. Small companies or startups don&apos;t benefit enough from GraphQL to justify the cost it brings. Typically speed and release quickly is the main driver for companies that those stages, and adopting something like tRPC might be a better choice. Of course, it is always possible to migrate to GraphQL later as a replacement (or even build on top of it?) at a later point.&lt;/p&gt;
&lt;p&gt;For medium-sized companies, who are already starting to see innovation slowdowns caused by an increasing number of REST APIs or hearing complaints from client teams about the backend, it might be time to start investing in GraphQL. There are so many benefits that come with GraphQL when done right.&lt;/p&gt;
&lt;p&gt;GraphQL would still be a massive help for large companies, but there might be limits to what it can solve at the scale. Instead of adopting a monolith GraphQL service, they should look at more scalable options such as schema-stitching with GraphQL Mesh or schema federation with Apollo Federation. We&apos;ll get into it at another time 🙂 &lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixing esbuild related CPU architecture error on Apple silicon Macs]]></title><description><![CDATA[<p>I ran into this issue recently where <strong>esbuild</strong> was complaining about the CPU architecture in VS Code. The project was running SvelteKit (v1) for some context. The error didn't disrupt anything I was doing, but I was sick of seeing the error message everywhere and decided to fix it. I've always run Rosetta v2 on my Apple silicon Macs because of compatibility concerns. Here's my article on <a href="/setting-up-zsh-with-vs-code-on-apple-silicon-mac-m1-chip">Setting up ZSH with VS Code on Apple silicon Mac (M1 chip)</a>. But this was the first time Rosetta caused any conflicts in my workflows. I found a fix at the end and decided to share it, given how little people have written about this.</p>]]></description><link>https://blog.hao.dev/fixing-esbuild-related-cpu-architecture-error-on-apple-silicon-macs</link><guid isPermaLink="false">https://blog.hao.dev/fixing-esbuild-related-cpu-architecture-error-on-apple-silicon-macs</guid><pubDate>Sun, 12 Feb 2023 15:33:00 GMT</pubDate><content:encoded>&lt;p&gt;I ran into this issue recently where &lt;strong&gt;esbuild&lt;/strong&gt; was complaining about the CPU architecture in VS Code. The project was running SvelteKit (v1) for some context. The error didn&apos;t disrupt anything I was doing, but I was sick of seeing the error message everywhere and decided to fix it. I&apos;ve always run Rosetta v2 on my Apple silicon Macs because of compatibility concerns. Here&apos;s my article on &lt;a href=&quot;/setting-up-zsh-with-vs-code-on-apple-silicon-mac-m1-chip&quot;&gt;Setting up ZSH with VS Code on Apple silicon Mac (M1 chip)&lt;/a&gt;. But this was the first time Rosetta caused any conflicts in my workflows. I found a fix at the end and decided to share it, given how little people have written about this.&lt;/p&gt;&lt;h2&gt;Esbuild complaining I had installed the wrong package for my platform&lt;/h2&gt;
&lt;p&gt;Everything went smoothly until I tried to add SCSS to the project, which I have covered in a previous article &lt;a href=&quot;/how-to-add-scss-or-sass-to-sveltekit&quot;&gt;How to add SCSS or SASS to SvelteKit&lt;/a&gt;. Later I tried to use TypeScript in the same project and saw the same error. In both cases, the error looked like this: &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;txt&quot;&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;You installed esbuild on another platform than the one you&apos;re currently using.
This won&apos;t work because esbuild is written with native code and needs to
install a platform-specific binary executable.

Specifically the &quot;@esbuild/darwin-x64&quot; package is present but this platform
needs the &quot;@esbuild/darwin-arm64&quot; package instead. People often get into this
situation by installing esbuild with npm running inside of Rosetta 2 and then
trying to use it with node running outside of Rosetta 2, or vice versa 
(Rosetta 2 is Apple&apos;s on-the-fly x86_64-to-arm64 translation service).

If you are installing with npm, you can try not copying the &quot;node_modules&quot;
directory when you copy the files over, and running &quot;npm ci&quot; or &quot;npm install&quot;
on the destination platform after the copy. Or you could consider using yarn
instead which has built-in support for installing a package on multiple
platforms simultaneously.

If you are installing with yarn, you can try listing both this platform and the
other platform in your &quot;.yarnrc.yml&quot; file using the &quot;supportedArchitectures&quot;
feature: https://yarnpkg.com/configuration/yarnrc/#supportedArchitectures
Keep in mind that this means multiple copies of esbuild will be present.

Another alternative is to use the &quot;esbuild-wasm&quot; package instead, which works
the same way on all platforms. But it comes with a heavy performance cost and
can sometimes be 10x slower than the &quot;esbuild&quot; package, so you may also not
want to do that.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Debugging&lt;/h2&gt;
&lt;h3&gt;First attempt - switch over from PNPM to Yarn&lt;/h3&gt;
&lt;p&gt;As shown in the error message, it recommends using Yarn due to its ability to support and configure multiple architectures via &lt;code class=&quot;language-text&quot;&gt;.yarnrc.yml&lt;/code&gt;. I was using PNPM at the time, so I thought this was what I was missing. So I tried switching over to Yarn, added the config file, and filled in the necessary configs, but sadly, it did nothing.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;supportedArchitectures&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;current&apos;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;cpu&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;arm64&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Second attempt - Switching over to ZSH shell instead of Rosetta shell&lt;/h3&gt;
&lt;p&gt;I thought it might be the way I am installing NPM packages. I have been running &lt;code class=&quot;language-text&quot;&gt;yarn install&lt;/code&gt; using Rosetta shell, which I&apos;ve configured to run in &lt;strong&gt;x64&lt;/strong&gt; CPU architecture mode instead of the default &lt;strong&gt;arm64&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s a screenshot proving Rosetta shell (the one on the right) is running in &lt;strong&gt;x64&lt;/strong&gt; architecture mode.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2bOySKdeRlZhsm5aLLkXwf/3d0f0e73bf6dabc6fd871f0acdea339e/Screenshot_2023-02-12_at_15.17.26.png&quot; alt=&quot;zsh and rosetta shells&quot;&gt;&lt;/p&gt;
&lt;p&gt;I checked my &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;, which doesn&apos;t include &lt;strong&gt;esbuild&lt;/strong&gt;, so it must be a nested dependency. After checking &lt;code class=&quot;language-text&quot;&gt;yarn.lock&lt;/code&gt; file, I was happy with that theory.&lt;/p&gt;
&lt;p&gt;Then I removed my &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder and installed it again. Unfortunately, nothing changed.&lt;/p&gt;
&lt;h3&gt;Third attempt - reinstalling node&lt;/h3&gt;
&lt;p&gt;Finally, I stumbled on to this &lt;a href=&quot;https://github.com/vercel/turbo/issues/1769#issuecomment-1239458815&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github issue comment&lt;/a&gt;, which suggested running &lt;code class=&quot;language-text&quot;&gt;node -p &quot;process.arch&quot;&lt;/code&gt; command to see which architecture &lt;strong&gt;Node&lt;/strong&gt; is running on. And both ZSH shell and Rosetta shell were returning &lt;strong&gt;x64&lt;/strong&gt;. So there is the issue.&lt;/p&gt;
&lt;h2&gt;The fix&lt;/h2&gt;
&lt;p&gt;I use NVM to manage my Node versions on my machine, so your fix may vary. The goal is to uninstall the current node and reinstall it using the terminal shell in &lt;strong&gt;arm64&lt;/strong&gt; mode. These were the commands I used, given I have NVM:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;nvm uninstall &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;
nvm &lt;span class=&quot;token function&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After that was done, I ran &lt;code class=&quot;language-text&quot;&gt;node -p &quot;process.arch&quot;&lt;/code&gt; in the ZSH shell it returned &lt;strong&gt;arm64&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The next step is to remove and reinstall all the node packages again.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;rm&lt;/span&gt; -rf node_modules
&lt;span class=&quot;token function&quot;&gt;yarn&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now check VS Code (it may need a reload), and the annoying errors should be gone!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What I learned about JS Date from writing a simple date library]]></title><description><![CDATA[<p>The built-in Date object is notoriously difficult to use. It is why there are several date libraries out there, such as <a href="https://day.js.org/" target="_blank" rel="nofollow noopener noreferrer">Day.js</a>, <a href="https://moment.github.io/luxon/#/" target="_blank" rel="nofollow noopener noreferrer">luxon</a>, <a href="https://date-fns.org/" target="_blank" rel="nofollow noopener noreferrer">date-fns</a>, etc. Recently, I've implemented a date library that acts as a light abstraction layer, providing syntactic sugar like the date libraries mentioned above. I won't go into detail about why I didn't just use one of those libraries in this article, and I will save that for another time. Instead, I want to discuss my learnings and recommendation after learnings from creating this date library.</p>]]></description><link>https://blog.hao.dev/what-i-learned-about-js-date-from-writing-a-simple-date-library</link><guid isPermaLink="false">https://blog.hao.dev/what-i-learned-about-js-date-from-writing-a-simple-date-library</guid><pubDate>Sun, 27 Nov 2022 18:05:00 GMT</pubDate><content:encoded>&lt;p&gt;The built-in Date object is notoriously difficult to use. It is why there are several date libraries out there, such as &lt;a href=&quot;https://day.js.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Day.js&lt;/a&gt;, &lt;a href=&quot;https://moment.github.io/luxon/#/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;luxon&lt;/a&gt;, &lt;a href=&quot;https://date-fns.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;date-fns&lt;/a&gt;, etc. Recently, I&apos;ve implemented a date library that acts as a light abstraction layer, providing syntactic sugar like the date libraries mentioned above. I won&apos;t go into detail about why I didn&apos;t just use one of those libraries in this article, and I will save that for another time. Instead, I want to discuss my learnings and recommendation after learnings from creating this date library.&lt;/p&gt;&lt;h2&gt;Best way to create a new date&lt;/h2&gt;
&lt;p&gt;Like most things in JavaScript, JS Date is very flexible when parsing different date formats. Unfortunately, flexibility often comes with its issues and drawbacks. Let&apos;s first take a look at what is possible.&lt;/p&gt;
&lt;p&gt;As you can see below, there are many different ways to create a new Date object.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; today &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; birthday &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;December 17, 1995 03:24:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// DISCOURAGED: may not work in all runtimes&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; birthday2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;1995-12-17T03:24:00&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This is ISO8601-compliant and will work reliably&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; birthday3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// the month is 0-indexed&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; birthday4 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1995&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; birthday5 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;628021800000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// passing epoch timestamp&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Borrowed from MDN docs&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After some struggles, I found the best way to create a Date object is using the ISO8601-compliant method.&lt;/p&gt;
&lt;h3&gt;Why ISO8601-compliant method?&lt;/h3&gt;
&lt;p&gt;I find this method the most straightforward to work with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It follows a strict format of &lt;code class=&quot;language-text&quot;&gt;YYYY-MM-DDTHH:mm:ss.sssZ&lt;/code&gt;, so there is no confusion around the date format, making it easy to read.&lt;/li&gt;
&lt;li&gt;It is still flexible, so we can still create dates without specific data, e.g. &lt;code class=&quot;language-text&quot;&gt;new Date(&quot;1995-12-17&quot;)&lt;/code&gt; or with the hour and minutes &lt;code class=&quot;language-text&quot;&gt;new Date (&quot;1992-12-17T12:50&quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We can easily convert a date into UTC by adding &lt;code class=&quot;language-text&quot;&gt;Z&lt;/code&gt; suffix, e.g. &lt;code class=&quot;language-text&quot;&gt;new Date(&quot;1992-12-17T12:50Z&quot;)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, I also noticed a few disadvantages of using this method.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, it is not as flexible as creating a Date with integers, which allows parsing partial time formats. When passed just the hour, &lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1992&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;11&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;17&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;1992-12-17T12:00:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;1992-12-17T12&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Second, it cannot infer an unknown date format. In the following example, it didn&apos;t know what to do when I passed 50 as the Date.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;50&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2000-02-19T00:00:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;2000-01-50&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// null&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Avoid parsing integers&lt;/h3&gt;
&lt;p&gt;Although this method is the most flexible when it comes to creating new dates. Here&apos;s a list of examples:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T00:00:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:00:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:12.000Z&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;333&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:12.333Z&quot; &lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The fact we always have to be mindful of month start from the index of 0 is a massive flaw. Many times, I needed to fix bugs around this when I forgot to subtract 1 when creating a new date. Here&apos;s a full explanation on &lt;a href=&quot;https://stackoverflow.com/a/41992352&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;StackOverflow&lt;/a&gt; if anyone is interested in knowing why this is the case.&lt;/p&gt;
&lt;p&gt;This method&apos;s second and most significant problem is a lack of an easy way to create a UTC date. Whilst there is a workaround, it is long-winded and makes it difficult to read. Not to mention how easy it is to forget to add the &lt;code class=&quot;language-text&quot;&gt;Date.UTC()&lt;/code&gt; part when creating a new date.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T00:00:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:00:00.000Z&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:00.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:12.000Z&quot; &lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;27&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;15&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;333&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// &quot;2022-11-27T15:40:12.333Z&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Avoid Epoch Timestamps&lt;/h3&gt;
&lt;p&gt;Passing dates as Epoch Timestamps was common when I worked with PHP around 2015. However, I haven&apos;t seen this method used at all since I moved over to using Node in the backend.&lt;/p&gt;
&lt;p&gt;First, one problem with this method is relying on the Timestamp to be in the correct time standard (e.g. UTC) to begin with. If we pass in a UTC timestamp, then we&apos;ll get a UTC date object, and vice-versa with non-UTC timestamps.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 954457200000&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getTime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 954460800000&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Secondly, timestamps are extremely difficult to comprehend for humans. There could be a bug in front of my eyes, and I wouldn&apos;t be able to spot it. So unless there is a solid reason to use this method of storing date, I would avoid it.&lt;/p&gt;
&lt;h3&gt;Avoid non-ISO8601-compliant string formats&lt;/h3&gt;
&lt;p&gt;Not sure I need to go into much detail regarding why &lt;code class=&quot;language-text&quot;&gt;new Date(&quot;December 17, 1995 03:24:00&quot;)&lt;/code&gt; is a bad idea. The lack of a strict standard makes creating new date inefficient at best and error-prone at worst.&lt;/p&gt;
&lt;h2&gt;UTC, GMT, ISO and local time&lt;/h2&gt;
&lt;p&gt;Time zones are confusing to work with and we are barely scratching the surfaces here with these acronyms.&lt;/p&gt;
&lt;h3&gt;What is the difference between UTC and GMT?&lt;/h3&gt;
&lt;p&gt;Coordinated Universal Time (UTC) is a non-location-based time standard. It means no country on Earth uses it as a local time. Instead, think of it as international time. Greenwich Mean Time (GMT) is a time zone in some European and African countries. In the UK, we change between GMT and British Summer Time (BST) depending on Daylight Saving Time (DST).&lt;/p&gt;
&lt;p&gt;It is always better to use UTC when possible. For example, it is a headache to consider time zones and daylight savings when we want to add two hours to a JS Date.&lt;/p&gt;
&lt;h3&gt;What is ISO Date?&lt;/h3&gt;
&lt;p&gt;International Organization for Standardisation (ISO) is an organisation that develops and publishes international standards. Essentially, they developed the concept of UTC.&lt;/p&gt;
&lt;p&gt;ISO is only referenced by JS Date as an output format, e.g. &lt;code class=&quot;language-text&quot;&gt;new Date().toISOString()&lt;/code&gt;, which returns a string like this &lt;code class=&quot;language-text&quot;&gt;&quot;2022-11-27T15:40:12.333Z&quot;&lt;/code&gt;. Since the value is always zero UTC offset, you could also think of it as &lt;code class=&quot;language-text&quot;&gt;.toUTCString()&lt;/code&gt; (this is something I made up!).&lt;/p&gt;
&lt;h3&gt;Local time&lt;/h3&gt;
&lt;p&gt;By default, JS Date will use the local device to figure out the timezone and use it as a base for date manipulations. This can be desirable since it makes displaying Date on the client side much more straightforward as now, as developers, we don&apos;t need to consider time zone.&lt;/p&gt;
&lt;p&gt;However, this approach has a massive problem if we ever need to work across different time zones. We also work across time zones more than you think. For example, clients and servers are often in different time zones. Sending a non-UTC date from a client to a server will usually result in bugs because the two sides have a very different understanding of the date&apos;s value.&lt;/p&gt;
&lt;h3&gt;Always use UTC&lt;/h3&gt;
&lt;p&gt;After running into several bugs, I finally decided to refactor my date library to always use UTC. One of the bugs I encountered was unit tests failing in the pipeline when they passed locally. This is because the pipeline&apos;s server was at a different timezone than my local machine.&lt;/p&gt;
&lt;p&gt;Another reason why I&apos;m so fixated on using UTC is that when we create a new date with non-UTC the output of &lt;code class=&quot;language-text&quot;&gt;getDate()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;getUTCDate()&lt;/code&gt; is different in the example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// code to get the number of days in the current month&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2022-04-29T23:00:00.000Z&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUTCDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 29 &amp;lt;= wrong&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 2022-04-30T00:00:00.000Z&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UTC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2022&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getUTCDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// 30 &amp;lt;= correct&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It&apos;s also worth mentioning that using UTC doesn&apos;t mean we lose the ability to display the correct Date and time within our applications. On the client side, we can use &lt;code class=&quot;language-text&quot;&gt;toLocaleDateString()&lt;/code&gt; to convert the UTC Date object back to any time zone we would like.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up ZSH with VS Code on Apple silicon Mac (M1 chip)]]></title><description><![CDATA[<p>I upgraded to MacBook Pro 14" a few months back. I've been using it for personal development and commercial contracts (working inside large teams) with no problem. For anyone who's on the fence about buying a Mac with Apple silicon (AKA M1 chip), I would encourage them to go for it. I want to point out that the lack of issues could be because I'm running all codebases with Resetta 2, an emulator that could translate Intel chip commands so they could run on Apple Silicon. In this article, I'll share my setup in case it helps anyone out there.</p>]]></description><link>https://blog.hao.dev/setting-up-zsh-with-vs-code-on-apple-silicon-mac-m1-chip</link><guid isPermaLink="false">https://blog.hao.dev/setting-up-zsh-with-vs-code-on-apple-silicon-mac-m1-chip</guid><pubDate>Sun, 10 Apr 2022 21:38:00 GMT</pubDate><content:encoded>&lt;p&gt;I upgraded to MacBook Pro 14&quot; a few months back. I&apos;ve been using it for personal development and commercial contracts (working inside large teams) with no problem. For anyone who&apos;s on the fence about buying a Mac with Apple silicon (AKA M1 chip), I would encourage them to go for it. I want to point out that the lack of issues could be because I&apos;m running all codebases with Resetta 2, an emulator that could translate Intel chip commands so they could run on Apple Silicon. In this article, I&apos;ll share my setup in case it helps anyone out there.&lt;/p&gt;&lt;h2&gt;A bit of history&lt;/h2&gt;
&lt;p&gt;You might be wondering why it is called Rosetta 2. It is because this is the second version of Rosetta, as the name indicates. Apple introduced the first version back in 2006 to help migrate users from PowerPC processors to Intel. So it is somewhat ironic that the first version of Rosetta brought Apple users to Intel, only to take them all away after 16 years with the second version.&lt;/p&gt;
&lt;h2&gt;Install Rosetta 2&lt;/h2&gt;
&lt;p&gt;If you have an Apple Silicon Mac, it should prompt you to install Rosetta when you try to open any app designed for Intel Macs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1akNL7h9wpxm0E5n2re0tZ/7b0dafab3a90b72146a397ed6b9fb254/Image.jpg&quot; alt=&quot;modal install rosetta mac&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you don&apos;t want to go through the trouble of first finding an app that is incompatible with your mac first, you can do it in the terminal just as easily:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;softwareupdate --install-rosetta --agree-to-license&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Install VS Code&lt;/h2&gt;
&lt;p&gt;It is worth noting that VS Code now natively support Apple Silicon. So the install process is the same as before - &lt;a href=&quot;https://code.visualstudio.com/download&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;download link&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Zsh&lt;/h2&gt;
&lt;p&gt;Zsh should be pre-installed on Macs now. So you shouldn&apos;t need to do anything here.&lt;/p&gt;
&lt;h2&gt;Setup Rosetta Terminal&lt;/h2&gt;
&lt;p&gt;We will duplicate the mac Terminal app at this stage and run one of them through Rosetta. This way, when we run any commands, it will be no different than running them on an Intel-based Mac.&lt;/p&gt;
&lt;p&gt;First, let&apos;s check the CPU architecture by running &lt;code class=&quot;language-text&quot;&gt;arch&lt;/code&gt; command in the terminal. Unless you have configured it already, it should say &lt;code class=&quot;language-text&quot;&gt;arm64&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ arch
arm64&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Open the Finder app on your app, then use the &lt;code class=&quot;language-text&quot;&gt;cmd + shift + u&lt;/code&gt; shortcut to get into the &lt;strong&gt;Utilities&lt;/strong&gt; folder.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/LDnptZsF8uBpxiWDaGYeG/e7f79e0617cc5d6a9a0886d3c870b262/Screenshot_2022-04-10_at_22.19.35.png&quot; alt=&quot;Mac Ultilities folder&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Now, right-click on the Terminal app and click on &lt;strong&gt;Duplicate&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/36KmIAe5naFRxCJBVO9scO/49e0ce37a57867758c6fa5471f5e4770/Screenshot_2022-04-10_at_22.16.49.png&quot; alt=&quot;Duplicate terminal app&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Then rename one of them to &lt;strong&gt;Rosetta Terminal&lt;/strong&gt; or whatever you want to call it.&lt;/li&gt;
&lt;li&gt;Right-click on the &lt;strong&gt;Rosetta Terminal&lt;/strong&gt; app and click on &lt;strong&gt;Get Info&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1akfyp5IdWp7IZbUxIxuwx/a13a90bce271d31fe92c85993ac7261a/Screenshot_2022-04-10_at_22.21.16.png&quot; alt=&quot;Terminal app get info&quot;&gt;&lt;/p&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Toggle on the &lt;strong&gt;Open using Rosetta&lt;/strong&gt; checkbox, and we are done!&lt;/li&gt;
&lt;/ol&gt;
&lt;img alt=&quot;Toggle rosetta on terminal app&quot; src=&quot;//images.ctfassets.net/zsjhz5fgria0/4YiUBC433qmQxpzaAP09Wa/1b69c2032974b1448e2e25487884b458/Screenshot_2022-04-10_at_22.23.26.png&quot; width=&quot;400&quot; /&gt;
&lt;p&gt;Open &quot;Rosetta Terminal&quot; and rerun the &lt;code class=&quot;language-text&quot;&gt;arch&lt;/code&gt; command. We can see now it says &lt;code class=&quot;language-text&quot;&gt;i386&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;$ arch
i386&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Configure VS Code&lt;/h2&gt;
&lt;p&gt;Open up VS Code and go to &lt;strong&gt;settings.json&lt;/strong&gt;, then paste in the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;{
  ...  
  &quot;terminal.integrated.profiles.osx&quot;: {
    &quot;rosetta&quot;: {
      &quot;path&quot;: &quot;arch&quot;,
      &quot;args&quot;: [&quot;-x86_64&quot;, &quot;zsh&quot;, &quot;-l&quot;],
      &quot;overrideName&quot;: true
    }
  },
  &quot;terminal.integrated.defaultProfile.osx&quot;: &quot;rosetta&quot;,
  ...
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then make sure the selected default is the rosetta profile, as shown below:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3wnSYOoxP9GzMIEZhcDvMd/4ba9c164da9a8096a03f03a5f58527b6/Screenshot_2022-04-10_at_22.33.27.png&quot; alt=&quot;VS Code default terminal profile&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Check everything is working&lt;/h2&gt;
&lt;p&gt;Finally, let&apos;s type &lt;code class=&quot;language-text&quot;&gt;arch&lt;/code&gt; command in the VS Code&apos;s integrated terminal. If you see &lt;code class=&quot;language-text&quot;&gt;i386&lt;/code&gt; then it is working correctly. Congrats!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up Vitest within SvelteKit repo]]></title><description><![CDATA[<p>SvelteKit doesn't come with unit test tools out of the box, so we'll have to configure our own. Usually, I'd add in Jest because it's always been my favourite testing framework. However, I recently heard good things about Vitest and thought I'd give it a try. For those unfamiliar with Vitest, it is a drop-in replacement for Jest and is much faster. In addition, I've noticed another benefit of how simple it is to configure compared to Jest since it natively supports TypeScript. However, we still need to do a couple of things to get Vitest working properly with SvelteKit. Let's go through what these things are.</p>]]></description><link>https://blog.hao.dev/setting-up-vitest-within-sveltekit-repo</link><guid isPermaLink="false">https://blog.hao.dev/setting-up-vitest-within-sveltekit-repo</guid><pubDate>Sun, 10 Apr 2022 19:51:00 GMT</pubDate><content:encoded>&lt;p&gt;SvelteKit doesn&apos;t come with unit test tools out of the box, so we&apos;ll have to configure our own. Usually, I&apos;d add in Jest because it&apos;s always been my favourite testing framework. However, I recently heard good things about Vitest and thought I&apos;d give it a try. For those unfamiliar with Vitest, it is a drop-in replacement for Jest and is much faster. In addition, I&apos;ve noticed another benefit of how simple it is to configure compared to Jest since it natively supports TypeScript. However, we still need to do a couple of things to get Vitest working properly with SvelteKit. Let&apos;s go through what these things are.&lt;/p&gt;&lt;h2&gt;Install Vitest&lt;/h2&gt;
&lt;p&gt;First, we need to install it. So go ahead with one of the following commands depending on the configuration of your project.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// with npm
npm i -D vitest

// or with yarn
yarn add -D vitest

// or with pnpm
pnpm add -D vitest&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you need more information, maybe follow the &lt;a href=&quot;https://vitest.dev/guide&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official guide&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Configure Vite/Vitest&lt;/h2&gt;
&lt;p&gt;Once Vitest has been configured, we need to update &lt;strong&gt;vite.config.ts&lt;/strong&gt; to look like the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;reference types=&quot;vitest&quot; /&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; defineConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vite&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; svelte &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;@sveltejs/vite-plugin-svelte&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;defineConfig&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	plugins&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;svelte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; hot&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;VITEST&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	test&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		globals&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		environment&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;happy-dom&apos;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice the &lt;code class=&quot;language-text&quot;&gt;/// &amp;lt;reference types=&quot;vitest&quot; /&gt;&lt;/code&gt;  at the top of the file in order to define test inside &lt;strong&gt;defineConfig&lt;/strong&gt;. If we don&apos;t add this, we&apos;ll get the following error message because the &lt;strong&gt;defineConfig&lt;/strong&gt; type doesn&apos;t understand the &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; property.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1Ptlt612OTa8LNQTSTOhnH/d0bbadbcbf0892f92530b27c65173cfb/Screenshot_2022-04-10_at_14.16.21.png&quot; alt=&quot;vitest erroring in vite config&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the full error message:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Argument of type &apos;{ plugins: Plugin[]; test: { globals: boolean; environment: string; }; }&apos; is not assignable to parameter of type &apos;UserConfigExport&apos;.
  Object literal may only specify known properties, and &apos;test&apos; does not exist in type &apos;UserConfigExport&apos;.ts(2345)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Configure TypeScript&lt;/h2&gt;
&lt;p&gt;Next, we need to configure TypeScript, so it understands the globals such as describe, test, or it. The &lt;strong&gt;tsconfig.json&lt;/strong&gt; should be just extending the default &lt;strong&gt;tsconfig&lt;/strong&gt; in svelte-kit. We want to add &lt;code class=&quot;language-text&quot;&gt;&quot;vitest/globals&quot;&lt;/code&gt; type in &lt;strong&gt;compilerOptions&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;extends&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./.svelte-kit/tsconfig.json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token property&quot;&gt;&quot;types&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;vitest/globals&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we don&apos;t do this, when you create or open a test file, you&apos;ll see the following errors. It is because typescript doesn&apos;t know where to look up these globals.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6FhKl1qwRTRNVcod5tHidd/30d4217ba211be61b4dc2bd48e4f0c65/Screenshot_2022-04-10_at_14.24.10.png&quot; alt=&quot;typescript complaining not knowing about vitest globals&quot;&gt;&lt;/p&gt;
&lt;p&gt;Again, here&apos;s the full error message:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Cannot find name &apos;describe&apos;. Do you need to install type definitions for a test runner? Try `npm i --save-dev @types/jest` or `npm i --save-dev @types/mocha`.ts(2582)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;All done&lt;/h2&gt;
&lt;p&gt;If you make those two small config file changes, everything should work as expected.&lt;/p&gt;
&lt;p&gt;I&apos;m having a blast with Vitest, love the improved SPEED! More importantly, coming from Jest, I didn&apos;t need to re-learn any new APIs. In fact, I don&apos;t even remember I&apos;m working with a different test framework most of the time.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to add SCSS or SASS to SvelteKit]]></title><description><![CDATA[<p>Although Svelte/SvelteKit support other CSS preprocessors, nothing comes baked-in so we need to manually install dependencies in order to use them in our project. In this blog post, we'll go over the simplest way to achieve this.</p>]]></description><link>https://blog.hao.dev/how-to-add-scss-or-sass-to-sveltekit</link><guid isPermaLink="false">https://blog.hao.dev/how-to-add-scss-or-sass-to-sveltekit</guid><pubDate>Sat, 09 Apr 2022 21:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Although Svelte/SvelteKit support other CSS preprocessors, nothing comes baked-in so we need to manually install dependencies in order to use them in our project. In this blog post, we&apos;ll go over the simplest way to achieve this.&lt;/p&gt;&lt;p&gt;For people who are new to Svelte or SvelteKit, the syntax for using SCSS or SASS is simple, just need to add lang=&quot;sass&quot; attribute to style tag.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;svelte&quot;&gt;&lt;pre class=&quot;language-svelte&quot;&gt;&lt;code class=&quot;language-svelte&quot;&gt;// SASS

&amp;lt;style lang=&amp;quot;sass&amp;quot;&amp;gt;
	.page {
		width: 100%;
		max-width: var(--column-width);
		margin: var(--column-margin-top) auto 0 auto;
	}
&amp;lt;/style&amp;gt;

// SCSS

&amp;lt;style lang=&amp;quot;scss&amp;quot;&amp;gt;
	.page {
		width: 100%;
		max-width: var(--column-width);
		margin: var(--column-margin-top) auto 0 auto;
	}
&amp;lt;/style&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, if I try to do this in a new SvelteKit repository, I&apos;ll see the following error message.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5bZmUAfpdrkks5fVxfEDT/15d9c66afd89751712cfe8e91ecffe05/Screenshot_2022-04-09_at_22.12.34.png&quot; alt=&quot;sveltekit-error-no-sass&quot;&gt;&lt;/p&gt;
&lt;p&gt;Here&apos;s the full error message:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Cannot find any of modules: sass,node-sass

Error: Cannot find module &apos;node-sass&apos;
Require stack:
- /Users/hao/workspace/demo/node_modules/.pnpm/svelte-preprocess@4.10.5_svelte@3.46.6+typescript@4.6.3/node_modules/svelte-preprocess/dist/modules/utils.js
- /Users/hao/workspace/demo/node_modules/.pnpm/svelte-preprocess@4.10.5_svelte@3.46.6+typescript@4.6.3/node_modules/svelte-preprocess/dist/autoProcess.js
- /Users/hao/workspace/demo/node_modules/.pnpm/svelte-preprocess@4.10.5_svelte@3.46.6+typescript@4.6.3/node_modules/svelte-preprocess/dist/index.js

The file cannot be parsed because style requires a preprocessor that doesn&apos;t seem to be setup or failed during setup. Did you setup a `svelte.config.js`? If you use SCSS, it may be necessary to add the path to your NODE runtime to the setting `svelte.language-server.runtime`, or use `sass` instead of `node-sass`. 

See https://github.com/sveltejs/language-tools/tree/master/docs#using-with-preprocessors for more info.svelte(style)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;All we have to do is add sass to our dev-dependencies, then SvelteKit will automatically pick it up.&lt;/p&gt;
&lt;p&gt;There are a few packages we can use to achieve this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/node-sass&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;node-sass&lt;/a&gt; is the one that is referenced the most, but since it has been &lt;a href=&quot;https://sass-lang.com/blog/libsass-is-deprecated&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;deprecated&lt;/a&gt; I strongly recommend you avoid using this.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/dart-sass&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;dart-sass&lt;/a&gt; is a more modern implementation of &lt;code class=&quot;language-text&quot;&gt;node-sass&lt;/code&gt;. But it has since been renamed to &lt;code class=&quot;language-text&quot;&gt;sass&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.npmjs.com/package/sass&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;sass&lt;/a&gt; is the one you should use!&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// NPM

npm i -D sass

// Yarn

yarn add -D sass

// PNPM

pnpm add -D sass&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then you should see the above error message disappear.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/zTkfbeq5hobMnAj6Dr6u8/f399c5571c8f22d5fddbfd2df8c6bb26/Screenshot_2022-04-09_at_22.39.42.png&quot; alt=&quot;sass sveltekit error fixed&quot;&gt;&lt;/p&gt;
&lt;p&gt;If the error message doesn&apos;t go away, try reloading your editor. If you use VS Code on a Mac, you can do this with cmd + p then search Developer: reload window.&lt;/p&gt;
&lt;p&gt;Note: you may also need to restart the SvelteKit app for this to take effect.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The best programming language to use in coding interviews]]></title><description><![CDATA[<p>As I've mentioned in a previous blog post <a href="/becoming-an-interview-engineer">Becoming an interview engineer</a>, I have been an interview engineer at <a href="http://karat.com" target="_blank" rel="nofollow noopener noreferrer">Karat</a> for about six months now. As an interviewer, it is my job to observe different candidates attempt the same questions. Over time, I noticed something intriguing - the best performing candidates I interviewed all use the same programming language. To make it clear, Karat allows candidates to pick from 26 programming languages (excluding Markdown) when attempting coding interviews. Out of these languages, candidates tend to choose the same few languages. Out of these languages, there's a clear winner when it comes to the best language to use for coding interviews - Python. Of course, candidates can still perform well using other languages, but I strongly feel the interviewing "game" is rigged against them.</p>]]></description><link>https://blog.hao.dev/the-best-programming-language-to-use-in-coding-interviews</link><guid isPermaLink="false">https://blog.hao.dev/the-best-programming-language-to-use-in-coding-interviews</guid><pubDate>Tue, 05 Apr 2022 23:34:00 GMT</pubDate><content:encoded>&lt;p&gt;As I&apos;ve mentioned in a previous blog post &lt;a href=&quot;/becoming-an-interview-engineer&quot;&gt;Becoming an interview engineer&lt;/a&gt;, I have been an interview engineer at &lt;a href=&quot;http://karat.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Karat&lt;/a&gt; for about six months now. As an interviewer, it is my job to observe different candidates attempt the same questions. Over time, I noticed something intriguing - the best performing candidates I interviewed all use the same programming language. To make it clear, Karat allows candidates to pick from 26 programming languages (excluding Markdown) when attempting coding interviews. Out of these languages, candidates tend to choose the same few languages. Out of these languages, there&apos;s a clear winner when it comes to the best language to use for coding interviews - Python. Of course, candidates can still perform well using other languages, but I strongly feel the interviewing &quot;game&quot; is rigged against them.&lt;/p&gt;&lt;p&gt;The best candidates I&apos;ve interviewed all used Python 3. I don&apos;t think the version of Python matters here, but I thought I&apos;d be accurate. So let&apos;s talk about why I think Python is the best language to use in coding interviews (if you are familiar with it already).&lt;/p&gt;
&lt;p&gt;Disclaimer: before people assume I&apos;m just a Python fanboy, I want to clarify that it isn&apos;t my language of choice, and it isn&apos;t something I use regularly. I can write basic scripts in Python, and it wouldn&apos;t be hard for me to pick it up again quickly. But I wouldn&apos;t put myself even close to the Python camp based on my skills and experience. So hopefully, that proves I&apos;m not being biased here.&lt;/p&gt;
&lt;h2&gt;Indentation &gt; curly-brace system&lt;/h2&gt;
&lt;p&gt; As the interviewer, I cannot say anything during interviews that directly helps the candidate, or they will get marked down for my comments. So imagine how painful it is for me to watch people spend a few minutes trying to fix a missing curly bracket.&lt;/p&gt;
&lt;p&gt;It might sound stupid to suggest brackets are a big deal. But when interviewing, people tend to make more mistakes due to the time pressure and knowing someone is watching them code. Our brains focus on the interview question and don&apos;t pick up simple syntax issues straight away. Coding slowly isn&apos;t an option to get the best marks since companies want fast and accurate solutions. When people rush, minor issues like missing brackets become harder to spot. I once watched someone spend 10 minutes struggling with a bracketing issue. Based on comments from other interviewers, I wasn&apos;t the only one either.&lt;/p&gt;
&lt;p&gt;If you are not convinced, I&apos;ve included some basic code snippets below to illustrate how the brackets can be confusing when coding under pressure. It is also worth noting that the code block tends to be more complex and be much longer in interviews, which makes spotting missing brackets even harder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# Python

def helloWorld:
	for i in range(len(some)):
		for j in range(len(random)):
			if falseCondition:
				for k in range(len(random)):
					...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// JavaScript

function helloWorld() {
	for (const i in some) {
		for (const j in random) {
			if (falseCondition) {
				for (const k in loop) {
					...	
				}
			}
                 }
           }
      }
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Did you notice there is one too many bracket in the JavaScript code?&lt;/p&gt;
&lt;h2&gt;Dynamic type = less code = code faster&lt;/h2&gt;
&lt;p&gt;When it comes to writing code fast, it is tough for statically typed language to complete with dynamically typed languages. Things as basic as declaring a function or variable are faster to do in Python than in a language like Java.&lt;/p&gt;
&lt;p&gt;Here is a comparison between declaring a function that takes in a 2D integer array and returns the same type in Python and Java, respectively.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# Python

def myFun(arr):&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// Java

public static int[][] myFun(int[][] arr) {&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Python developer has already started implementing the function by the time Java developer types out the word &quot;static&quot;. Yes, this is just one line of code, but Python code is always shorter with the same approach. A few characters here and there don&apos;t sound much. But when there are tens or hundreds of lines of code, it all adds up because it gives a developer more time to be debugging code instead of typing things out.&lt;/p&gt;
&lt;p&gt;Longer code also means more likely for mistakes to creep in. For example, people make mistakes with function data types or forget to type the &quot;static&quot; keyword when declaring the function. With dynamically typed languages, you simply don&apos;t have these concerns. Even compared to another dynamically typed language like JavaScript, I have to admit Python code is a lot faster to write because it requires fewer characters to implement the same logic.&lt;/p&gt;
&lt;p&gt;&quot;Statically typed languages have advantages too!&quot; I hear you say. That&apos;s 100% true, and I agree. Personally, I went from extremely against statically typed language at the start of my career to starting every single project now using TypeScript. Unless I&apos;m throwing something together quickly, TypeScript gets my vote every time. Types come with safety and guarantee, and IDE can leverage it to give better autocomplete, it can catch issues that dynamically typed language cannot, and plenty more advantages. But when shorter code beats good code in an interview, dynamically typed languages win hands down. Like many places, at Karat, we put much more emphasis on working solutions than nice-looking code.&lt;/p&gt;
&lt;h2&gt;Some languages are better for implementing algorithmic questions&lt;/h2&gt;
&lt;p&gt;Maybe it&apos;s because I&apos;m a JavaScript developer, so this is a more significant pain point. For others languages, this might be irrelevant. But, as a JavaScript developer, I find the lack of useful util functions makes coding algorithm solutions significantly harder.&lt;/p&gt;
&lt;p&gt;To demonstrate my point further, here is a comparison between Python and JavaScript for finding the intersections between two arrays. It is not just that the Python code is shorter; the JavaScript code is also harder to read and understand. All this adds friction to the interview process.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;# Python

def intersection(a, b):
    return list(set(a).intersection(b))&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// JavaScript

function getIntersection(a, b) {
    const set1 = new Set(a);
    const set2 = new Set(b);

    const intersection = [...set1].filter(
        element =&gt; set2.has(element)
    );

    return intersection;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Would I find an intersection like this in an interview when coding in JavaScript? Of course not. My first choice will be to implement the solution using &lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt; loop because I&apos;m more familiar with it. In interviews, familiarity is king. But this implementation isn&apos;t any quicker to write and could introduce unnecessary bugs than the Python approach. It is also inferior in terms of time complexity.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;function getIntersection(a,b) {
	const arr = [];

	a.forEach(elem =&gt; {
		if (b.contains(elem)) {
			arr.push(elem);
		}
	}

	const arr;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;To ensure this isn&apos;t a problem that only partially affects JavaScript developers, I researched online and found this question on &lt;a href=&quot;https://stackoverflow.com/questions/44956031/how-to-get-intersection-of-two-slice-in-golang&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;stackoverflow&lt;/a&gt;, so it seems GoLang has a similar problem.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Now, should you drop everything and learn Python for all future interviews? No, that would be crazy. People can be good at any language they spend time mastering. The better thing to do is practice with more interview-style coding questions and find a solution faster. Finding the most straightforward solution will save you more time than switching to Python and typing less code. It is often best to go with something more familiar and comfortable than to min-max the benefits I explained above in time-sensitive environments. However, for people who already use multiple languages where Python is already in their toolbox, I&apos;d strongly recommend using it for interviews unless the job requires a specific language.&lt;/p&gt;
&lt;p&gt;Some candidates ask me how come I became an interview engineer. It always sounds like a cliche, but I enjoy seeing different people approach the same interview questions from different angles and coming up with unique solutions. Furthermore, I often learn something from the candidates I&apos;ve interviewed. Believe it or not, an interview I recently conducted inspired this blog post. If this sounds like something you are interested in, you can apply to become an interview engineer using &lt;a href=&quot;https://grnh.se/737338f42us&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this link&lt;/a&gt; (if you are from the US) or &lt;a href=&quot;https://grnh.se/bf36b7862us&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this link&lt;/a&gt; (from any other country)!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Becoming an interview engineer]]></title><description><![CDATA[<p>A few months ago, I was approached by a recruiter telling me about this fully remote job at <a href="https://karat.com" target="_blank" rel="nofollow noopener noreferrer">Karat</a> that offers decent pay. If you haven't heard of Karat before, it is the world's largest interviewing company. They use a global network of professional software to conduct technical interviews for companies, some of which you may have heard of before: Robinhood, Indeed, Roblox, Coinbase and more. Of course, at the time, I didn't know this, but the recruiter hooked me on the idea of setting my hours to work part-time or full time. After receiving several emails, I finally decided to reach out to hear more about the role. The phone conversation assured me that the role was an excellent way to earn extra money and get interviewer experience. I wanted to do it part-time, so I could always stop on my terms if it got too demanding.</p>]]></description><link>https://blog.hao.dev/becoming-an-interview-engineer</link><guid isPermaLink="false">https://blog.hao.dev/becoming-an-interview-engineer</guid><pubDate>Wed, 30 Mar 2022 21:05:00 GMT</pubDate><content:encoded>&lt;p&gt;A few months ago, I was approached by a recruiter telling me about this fully remote job at &lt;a href=&quot;https://karat.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Karat&lt;/a&gt; that offers decent pay. If you haven&apos;t heard of Karat before, it is the world&apos;s largest interviewing company. They use a global network of professional software to conduct technical interviews for companies, some of which you may have heard of before: Robinhood, Indeed, Roblox, Coinbase and more. Of course, at the time, I didn&apos;t know this, but the recruiter hooked me on the idea of setting my hours to work part-time or full time. After receiving several emails, I finally decided to reach out to hear more about the role. The phone conversation assured me that the role was an excellent way to earn extra money and get interviewer experience. I wanted to do it part-time, so I could always stop on my terms if it got too demanding.&lt;/p&gt;&lt;p&gt;When I spoke with the recruiter on the phone, he mentioned that I&apos;d have to go through this mandatory boot camp (which lasts just over a week) before I could start conducting interviews. This training period is billable, which means they effectively pay people to learn. How brilliant is this! This boot camp aims to teach new joiners basic interviewing skills and learn the basics to provide a good Karat style interview experience for candidates.&lt;/p&gt;
&lt;p&gt;Note: later, I found out the learning track I&apos;m about to explain isn&apos;t the standard process at Karat. I joined around Christmas time, which meant a peak period for grad interviews. So this track is unsurprisingly called &quot;uni track&quot; (I can&apos;t remember the exact wording). We focused on learnings relevant to interviewing candidates for internships or grad jobs.&lt;/p&gt;
&lt;h2&gt;The boot camp&lt;/h2&gt;
&lt;p&gt;Getting setup was pretty easy, Karat sent me some links, and I registered. They also sent me Google Docs links that contained schedules and learning materials in various formats. The materials cover topics such as how to be a good interviewer, explained important principles we had to follow and what to do in various situations.&lt;/p&gt;
&lt;p&gt;Most of the training was self-learning, which was perfect because I could plan it around my free time. Some events were mandatory to attend, but most of them were in the late afternoon for me due to timezone differences. Maybe I could have managed it without taking time off work, but I booked days off just in case.&lt;/p&gt;
&lt;p&gt;One thing the recruiter didn&apos;t mention was that there&apos;d be an assessment at the end of the boot camp to determine whether they keep me on or not, which makes complete sense. Just weird the recruiter didn&apos;t mention it to me. It&apos;s not like I won&apos;t find out since it was the first thing they said to us in the induction session. To pass the boot camp and get accepted, I had to conduct two mock interviews as the interviewer. These two interviews will be reviewed and marked, and then finally, a decision will be made.&lt;/p&gt;
&lt;h3&gt;Day 1&lt;/h3&gt;
&lt;p&gt;The first day was straightforward. I only had to join a mandatory induction meeting which lasted about an hour. After that, I was free to plan the rest of my day, most of which I spent reading the learning materials. I wanted to do well in this boot camp since this job opportunity had a lot to offer, and getting kicked out before I even got in the door will feel pretty crappy. As a result, I spent around 5 hours learning the materials on top of the induction session.&lt;/p&gt;
&lt;h3&gt;Day 2&lt;/h3&gt;
&lt;p&gt;On the second day, the plan was to finish any self-learning by the end of the day. Since Day 3 will primarily focus on practice for the assessment interviews, where we&apos;ll use everything in the materials. I finished the self-learning materials and then went back and noted areas I felt were important.&lt;/p&gt;
&lt;p&gt;In addition, other bits and pieces happened on this day too. Such as preparing for practice interviews and submitting interview question answers to prove we are familiar with them. All in all, this took me somewhere between 4 - 5 hours complete.&lt;/p&gt;
&lt;h3&gt;Day 3&lt;/h3&gt;
&lt;p&gt;A Karat&apos;s mentor hosted a two-hour video call to explain the next steps and answer questions we had from the previous two days. Then everyone got randomly allocated into pairs, so we could each take turns practising interviewing each other to get familiar with the interview format and style. Craig and I got paired together, and it turns out we both wanted to do well in the upcoming assessments. So we practised everything we learned from each other, testing each other on mock questions and discussing them in great detail. We spent nearly 3 hours on this call, which was supposed to take less than two hours. But I felt more confident by the end of it.&lt;/p&gt;
&lt;h3&gt;Day 4&lt;/h3&gt;
&lt;p&gt;It is the day for us to complete the first assessment interview. Karat already scheduled time slots for two sessions. Like the practice, people in the boot camp will play interviewer in one session and candidate in the other. According to the schedule, I first play the candidate for someone else, then later take on the interviewer role.&lt;/p&gt;
&lt;p&gt;I joined the call a few minutes early, but my &quot;interviewer&quot; never showed. After a few minutes, I messaged the person (my partner) to see if he had forgotten or wished to push the interview back for whatever reason. He responded and said he was joining. A couple more minutes later, he joined but dropped off almost immediately due to internet issues. After some back and forth, Karat&apos;s support person decided to postpone this until my partner&apos;s internet gets better, which is likely to be the next day. It sucked for me because I was all mentally prepared.&lt;/p&gt;
&lt;p&gt;Whilst I waited for the assessment interview, I created my interview script to use during interviews to make sure I followed the structure and format of the interview correctly going forward. Maybe I should have thanked the person I was initially paired with because it has proven very handy later.&lt;/p&gt;
&lt;p&gt;A couple of hours later, they found someone willing to play the candidate for me later during the day. This time it is an experienced interviewer and not someone from the boot camp. During the assessment interview, she played a tricky candidate on purpose and pushed me to my limits. I was surprised but handled the &quot;unexpected&quot; events relatively well during the interview. After the interview, I spent about an hour on the mandatory write up where I had to describe and mark the candidate&apos;s performance.
Later, I attended an optional Group Q&amp;#x26;A session, which lasted about an hour and a half. Only a handful of us attended, so we are got plenty of opportunities to ask questions.&lt;/p&gt;
&lt;p&gt;Based on the schedule, this was supposed to be a short day. But in reality, at least for me, it was full of surprises due to my original assessment getting delayed. As a result, I spent around 10 hours on everything mentioned above. It was quite an exhausting day.&lt;/p&gt;
&lt;h3&gt;Day 5&lt;/h3&gt;
&lt;p&gt;The day&apos;s main activity was to conduct another practice interview, again once as a candidate and another as interviewer. Since Craig and I got on so well, we requested to be paired together again. Like last time, we went through the exercise without any drama. First, we went through our assessment interviews as part of this call, and then we took a step further by sharing our feedback results and analysing them together.
I managed to get everything done in roughly 3 hours.&lt;/p&gt;
&lt;h3&gt;Day 6&lt;/h3&gt;
&lt;p&gt;It&apos;s time for the second assessment. I had a different partner this time, and fortunately, there was no drama. We went through the two interviews quickly (two hours in total). Like the previous assessment, I played candidate in one interview and then interviewer in another.&lt;/p&gt;
&lt;p&gt;After the two interviews, I spent about an hour on the interview write-up. So, in total, I completed everything in 3 hours.&lt;/p&gt;
&lt;h3&gt;Day 7&lt;/h3&gt;
&lt;p&gt;We all waited around for news about the final result.&lt;/p&gt;
&lt;h2&gt;After boot camp&lt;/h2&gt;
&lt;p&gt;A few days later, I heard back from Karat that I had passed the assessment. I was relieved that I got through and slightly nervous about interviewing real candidates. Fast forward a few months; I&apos;ve completed  40 interviews since then. It&apos;s been a unique educational experience. I learned how to conduct good interviews adapted to the candidate, and the real-world experience was also invaluable. But I won&apos;t go into more detail in this blog post.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;You never really know something until you teach it to someone else.&quot; - John Maxwell&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;When looking back at my experience at Karat, the above quote comes to mind. It couldn&apos;t be more true when it comes to technical interviews. I never realised before that interviews are more than just asking questions and checking if the candidate gave the correct answer. Conducting interviews at a pace that is best suited for the candidate, making the candidate feel comfortable to showcase the best versions of themselves, effectively presenting questions, and more are just some of the challenges I faced. I guess what I&apos;m trying to say is interviewing someone is easy; providing a good interview experience for every candidate is hard.&lt;/p&gt;
&lt;p&gt;I won&apos;t lie; the money caught my attention first and is still an excellent incentive. Working from anywhere in the world with a good internet connection and setting my hours feels like a dream. Honestly, I can&apos;t ask for a better part-time job. But through the boot camp and the interviews that followed, I realised I still had a lot to learn.&lt;/p&gt;
&lt;p&gt;So, what&apos;s next?&lt;/p&gt;
&lt;p&gt;As mentioned already, I&apos;ve conducted 40 interviews so far. I was also lucky enough to get promoted to senior interview engineer a few months back. I will continue with this part-time role, and my next goal is to reach 100 interviews!&lt;/p&gt;
&lt;p&gt;If this sounds interesting to you and it&apos;s something you want to try out, you can apply using &lt;a href=&quot;https://grnh.se/737338f42us&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this link&lt;/a&gt; (if you are from the US) or &lt;a href=&quot;https://grnh.se/bf36b7862us&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this link&lt;/a&gt; (from any other country)!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Software field salary guide for looking for a new job in 2022]]></title><description><![CDATA[<p>Are you thinking of switching jobs or moving to a different role altogether in 2022? Knowing what salary to ask for is always tricky since there are a lot of factors at play. But having a rough figure might at least put you in the right ballpark and avoid that mistake of saying yes to an underpaid job. I came by this list of salaries for different roles in the software development domain. From what I can tell the figures in these tables are pretty accurate. Hope you find it helpful!</p>]]></description><link>https://blog.hao.dev/software-field-salary-guide-for-looking-for-a-new-job-in-2022</link><guid isPermaLink="false">https://blog.hao.dev/software-field-salary-guide-for-looking-for-a-new-job-in-2022</guid><pubDate>Sun, 06 Feb 2022 18:27:00 GMT</pubDate><content:encoded>&lt;p&gt;Are you thinking of switching jobs or moving to a different role altogether in 2022? Knowing what salary to ask for is always tricky since there are a lot of factors at play. But having a rough figure might at least put you in the right ballpark and avoid that mistake of saying yes to an underpaid job. I came by this list of salaries for different roles in the software development domain. From what I can tell the figures in these tables are pretty accurate. Hope you find it helpful!&lt;/p&gt;&lt;p&gt;All these are UK salaries, so may not apply in other countries.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Software&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Principle Software Engineer&lt;/td&gt;
&lt;td&gt;£110k - £150k&lt;/td&gt;
&lt;td&gt;£750 - £900&lt;/td&gt;
&lt;td&gt;£800 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lead Software Engineer&lt;/td&gt;
&lt;td&gt;£90k - £115k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£750 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Java Engineer&lt;/td&gt;
&lt;td&gt;£75k - £90k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£700 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Java Engineer&lt;/td&gt;
&lt;td&gt;£60k - £75k&lt;/td&gt;
&lt;td&gt;£450 - £500&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Frontend Engineer&lt;/td&gt;
&lt;td&gt;£75k - £95k&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend Engineer&lt;/td&gt;
&lt;td&gt;£45k - £60k&lt;/td&gt;
&lt;td&gt;£400 - £500&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior .NET Engineer&lt;/td&gt;
&lt;td&gt;£70k - £100k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.NET Engineer&lt;/td&gt;
&lt;td&gt;£45k - £60k&lt;/td&gt;
&lt;td&gt;£450 - £500&lt;/td&gt;
&lt;td&gt;£600 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Node.js Engineer&lt;/td&gt;
&lt;td&gt;£70k - £95k&lt;/td&gt;
&lt;td&gt;£550 - £750&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Node.js Engineer&lt;/td&gt;
&lt;td&gt;£45k - £70k&lt;/td&gt;
&lt;td&gt;£500 - £550&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Scala Engineer&lt;/td&gt;
&lt;td&gt;£80k - £95k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£750 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scala Engineer&lt;/td&gt;
&lt;td&gt;£60k - £80k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£675 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Golang Engineer&lt;/td&gt;
&lt;td&gt;£70k - £95k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£700 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Golang Engineer&lt;/td&gt;
&lt;td&gt;£50k - £70k&lt;/td&gt;
&lt;td&gt;£550 - £600&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Kotlin Engineer&lt;/td&gt;
&lt;td&gt;£85k - £95k&lt;/td&gt;
&lt;td&gt;£700 - £850&lt;/td&gt;
&lt;td&gt;£700 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kotlin Engineer&lt;/td&gt;
&lt;td&gt;£70k - £80k&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior PHP Engineer&lt;/td&gt;
&lt;td&gt;£55k - £80k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PHP Engineer&lt;/td&gt;
&lt;td&gt;£40k - £55k&lt;/td&gt;
&lt;td&gt;£400 - £500&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Python Engineer&lt;/td&gt;
&lt;td&gt;£70k - £100k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£700 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Python Engineer&lt;/td&gt;
&lt;td&gt;£50k - £70k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lead Data Engineer&lt;/td&gt;
&lt;td&gt;£90k - £120k&lt;/td&gt;
&lt;td&gt;£700 - £800&lt;/td&gt;
&lt;td&gt;£800 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Data Engineer&lt;/td&gt;
&lt;td&gt;£80k - £100k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£750 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Engineer&lt;/td&gt;
&lt;td&gt;£50k - £75k&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior iOS Engineer&lt;/td&gt;
&lt;td&gt;£75k - £90k&lt;/td&gt;
&lt;td&gt;£450 - £650&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;iOS Engineer&lt;/td&gt;
&lt;td&gt;£50k - £75k&lt;/td&gt;
&lt;td&gt;£300 - £450&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Android Engineer&lt;/td&gt;
&lt;td&gt;£75k - £90k&lt;/td&gt;
&lt;td&gt;£450 - £650&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Android Engineer&lt;/td&gt;
&lt;td&gt;£50k - £75k&lt;/td&gt;
&lt;td&gt;£300 - £450&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior React Native Engineer&lt;/td&gt;
&lt;td&gt;£65k - £95k&lt;/td&gt;
&lt;td&gt;£550 - £750&lt;/td&gt;
&lt;td&gt;£700 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;React Native Engineer&lt;/td&gt;
&lt;td&gt;£40k - £65k&lt;/td&gt;
&lt;td&gt;£500 - £550&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Flutter Engineer&lt;/td&gt;
&lt;td&gt;£80k - £100k&lt;/td&gt;
&lt;td&gt;£550 - £750&lt;/td&gt;
&lt;td&gt;£700 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Flutter Engineer&lt;/td&gt;
&lt;td&gt;£50k - £80k&lt;/td&gt;
&lt;td&gt;£500 - £550&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior C++ Engineer&lt;/td&gt;
&lt;td&gt;£45k - £75k&lt;/td&gt;
&lt;td&gt;£400 - £700&lt;/td&gt;
&lt;td&gt;£750 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;C++ Engineer&lt;/td&gt;
&lt;td&gt;£30k - £45k&lt;/td&gt;
&lt;td&gt;£300 - £350&lt;/td&gt;
&lt;td&gt;£550 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Ruby Engineer&lt;/td&gt;
&lt;td&gt;£65k - £85k&lt;/td&gt;
&lt;td&gt;£450 - £600&lt;/td&gt;
&lt;td&gt;£650 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Ruby Engineer&lt;/td&gt;
&lt;td&gt;£45k - £65k&lt;/td&gt;
&lt;td&gt;£350 - £450&lt;/td&gt;
&lt;td&gt;£450 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Clojure Engineer&lt;/td&gt;
&lt;td&gt;£75k - £95k&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;td&gt;£700 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Clojure Engineer&lt;/td&gt;
&lt;td&gt;£60k - £80k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junior Engineer&lt;/td&gt;
&lt;td&gt;£35k - £55k&lt;/td&gt;
&lt;td&gt;£350 - £450&lt;/td&gt;
&lt;td&gt;£450 - £550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grad Engineer&lt;/td&gt;
&lt;td&gt;£25k - £35k&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;td&gt;-&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;DevOps &amp;#x26; Infra&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;DevOps Manager&lt;/td&gt;
&lt;td&gt;£90k - £120k&lt;/td&gt;
&lt;td&gt;£700 - £1000&lt;/td&gt;
&lt;td&gt;£800 - £1200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior DevOps Engineer&lt;/td&gt;
&lt;td&gt;£75k - £110k&lt;/td&gt;
&lt;td&gt;£650 - £800&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DevOps Engineer&lt;/td&gt;
&lt;td&gt;£50k - £70k&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;td&gt;£600 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junior DevOps Engineer&lt;/td&gt;
&lt;td&gt;£35k - £50k&lt;/td&gt;
&lt;td&gt;£400 - £500&lt;/td&gt;
&lt;td&gt;£500 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grad DevOps&lt;/td&gt;
&lt;td&gt;£25k - £40k&lt;/td&gt;
&lt;td&gt;£200 - £300&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior System Admin&lt;/td&gt;
&lt;td&gt;£60k - £90k&lt;/td&gt;
&lt;td&gt;£500 - £700&lt;/td&gt;
&lt;td&gt;£600 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;System Admin&lt;/td&gt;
&lt;td&gt;£40k - £50k&lt;/td&gt;
&lt;td&gt;£350 - £500&lt;/td&gt;
&lt;td&gt;£450 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junior System Admin&lt;/td&gt;
&lt;td&gt;£30k - £40k&lt;/td&gt;
&lt;td&gt;£300 - £350&lt;/td&gt;
&lt;td&gt;£400 - £550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grad System Admin&lt;/td&gt;
&lt;td&gt;£25k - £40k&lt;/td&gt;
&lt;td&gt;£200 - £300&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Application Support&lt;/td&gt;
&lt;td&gt;£40k - £70k&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;td&gt;£400 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Security &amp;#x26; Network Enginner&lt;/td&gt;
&lt;td&gt;£50k - £100k&lt;/td&gt;
&lt;td&gt;£400 - £750&lt;/td&gt;
&lt;td&gt;£500 - £950&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SRE Engineer&lt;/td&gt;
&lt;td&gt;£60k - £120k&lt;/td&gt;
&lt;td&gt;£500 - £750&lt;/td&gt;
&lt;td&gt;£600 - £950&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database Admin&lt;/td&gt;
&lt;td&gt;£50k - £100k&lt;/td&gt;
&lt;td&gt;£300 - £600&lt;/td&gt;
&lt;td&gt;£450 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;QA &amp;#x26; Testing&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Lead Tester Engineer&lt;/td&gt;
&lt;td&gt;£80k - £95k&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;td&gt;£700 - £950&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Tester Engineer&lt;/td&gt;
&lt;td&gt;£60k - £80k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tester Engineer&lt;/td&gt;
&lt;td&gt;£40k - £60k&lt;/td&gt;
&lt;td&gt;£400 - £500&lt;/td&gt;
&lt;td&gt;£500 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junior Tester Engineer&lt;/td&gt;
&lt;td&gt;£30k - £40k&lt;/td&gt;
&lt;td&gt;£300 - £400&lt;/td&gt;
&lt;td&gt;£400 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grad Tester Engineer&lt;/td&gt;
&lt;td&gt;£25k - £35k&lt;/td&gt;
&lt;td&gt;£250 - £300&lt;/td&gt;
&lt;td&gt;£350 - £500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior SDET&lt;/td&gt;
&lt;td&gt;£70k - £90k&lt;/td&gt;
&lt;td&gt;£550 - £700&lt;/td&gt;
&lt;td&gt;£650 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;SDET&lt;/td&gt;
&lt;td&gt;£50k - £70k&lt;/td&gt;
&lt;td&gt;£450 - £550&lt;/td&gt;
&lt;td&gt;£550 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Junior SDET&lt;/td&gt;
&lt;td&gt;£30k - £45k&lt;/td&gt;
&lt;td&gt;£300 - £400&lt;/td&gt;
&lt;td&gt;£400 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grad SDET&lt;/td&gt;
&lt;td&gt;£25k - £35k&lt;/td&gt;
&lt;td&gt;£250 - £300&lt;/td&gt;
&lt;td&gt;£350 - £500&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Here are also some salaries and rates for other roles related to software development.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architects&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Solution Architect&lt;/td&gt;
&lt;td&gt;£95k - £130k&lt;/td&gt;
&lt;td&gt;£600 - £750&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical Architect&lt;/td&gt;
&lt;td&gt;£100k - £140k&lt;/td&gt;
&lt;td&gt;£700 - £900&lt;/td&gt;
&lt;td&gt;£900 - £1200&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Product, UX &amp;#x26; Design&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Senior Product Manager/Owner&lt;/td&gt;
&lt;td&gt;£75k - £100k&lt;/td&gt;
&lt;td&gt;£550 - £800&lt;/td&gt;
&lt;td&gt;£600 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product Manager/Owner&lt;/td&gt;
&lt;td&gt;£60k - £75k&lt;/td&gt;
&lt;td&gt;£400 - £550&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior UX Designer&lt;/td&gt;
&lt;td&gt;£65k - £85k&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UX Designer&lt;/td&gt;
&lt;td&gt;£45k - £65k&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior UI Designer&lt;/td&gt;
&lt;td&gt;£65k - £85k&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;UI Designer&lt;/td&gt;
&lt;td&gt;£45k - £65k&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;td&gt;£450 - £550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Product Designer&lt;/td&gt;
&lt;td&gt;£65k - £85k&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;td&gt;£600 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Product Designer&lt;/td&gt;
&lt;td&gt;£45k - £65k&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;User Researcher&lt;/td&gt;
&lt;td&gt;£45k - £65k&lt;/td&gt;
&lt;td&gt;£400 - £600&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Delivery&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Delivery Manager&lt;/td&gt;
&lt;td&gt;£70k - £110k&lt;/td&gt;
&lt;td&gt;£450 - £750&lt;/td&gt;
&lt;td&gt;£550 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Programme Manager&lt;/td&gt;
&lt;td&gt;£80k - £120k&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;td&gt;£700 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Project Manager&lt;/td&gt;
&lt;td&gt;£70k - £90k&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;td&gt;£650 - £750&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project Manager&lt;/td&gt;
&lt;td&gt;£50k - £65k&lt;/td&gt;
&lt;td&gt;£400 - £550&lt;/td&gt;
&lt;td&gt;£500 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Senior Business Analyst&lt;/td&gt;
&lt;td&gt;£75k - £85k&lt;/td&gt;
&lt;td&gt;£525 - £600&lt;/td&gt;
&lt;td&gt;£575 - £675&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Business Analyst&lt;/td&gt;
&lt;td&gt;£50k - £70k&lt;/td&gt;
&lt;td&gt;£300 - £550&lt;/td&gt;
&lt;td&gt;£400 - £650&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Agile Coach&lt;/td&gt;
&lt;td&gt;£80k - £120k&lt;/td&gt;
&lt;td&gt;£600 - £1000&lt;/td&gt;
&lt;td&gt;£700 - £1200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scrum Master&lt;/td&gt;
&lt;td&gt;£60k - £80k&lt;/td&gt;
&lt;td&gt;£400 - £600&lt;/td&gt;
&lt;td&gt;£500 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Project Coordinator&lt;/td&gt;
&lt;td&gt;£25k - £40k&lt;/td&gt;
&lt;td&gt;£200 - £350&lt;/td&gt;
&lt;td&gt;£250 - £400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Senior Appointments&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;CTO/CIO/CPO&lt;/td&gt;
&lt;td&gt;£160k - £230k&lt;/td&gt;
&lt;td&gt;£1000 - £1500&lt;/td&gt;
&lt;td&gt;£1400 - £2000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Early Stage Startup CTO&lt;/td&gt;
&lt;td&gt;£120k - £170k&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;td&gt;£900 - £1400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VP/Head of Engineering&lt;/td&gt;
&lt;td&gt;£120k - £175k&lt;/td&gt;
&lt;td&gt;£800 - £1000&lt;/td&gt;
&lt;td&gt;£1100 - £1600&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Engineering Manager&lt;/td&gt;
&lt;td&gt;£95k - £130k&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;td&gt;£900 - £1200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VP/Head of Product&lt;/td&gt;
&lt;td&gt;£120k - £180k&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;td&gt;£950 - £1400&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Chief/Head of Architecture&lt;/td&gt;
&lt;td&gt;£120k - £160k&lt;/td&gt;
&lt;td&gt;£850 - £1000&lt;/td&gt;
&lt;td&gt;£1000 - £1500&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head of Infra/DevOps/Cloud&lt;/td&gt;
&lt;td&gt;£100k - £150k&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;td&gt;£900 - £1300&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head of Project Management&lt;/td&gt;
&lt;td&gt;£85k - £120k&lt;/td&gt;
&lt;td&gt;£550 - £650&lt;/td&gt;
&lt;td&gt;£700 - £900&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head of QA/Testing&lt;/td&gt;
&lt;td&gt;£80k - £110k&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;td&gt;£800 - £1100&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Talent Acquisition &amp;#x26; HR&lt;/th&gt;
&lt;th&gt;Permanent&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (outside IR35)&lt;/th&gt;
&lt;th&gt;Contractor Day Rate (inside IR35)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VP People&lt;/td&gt;
&lt;td&gt;£90k - £120&lt;/td&gt;
&lt;td&gt;£600 - £800&lt;/td&gt;
&lt;td&gt;£750 - £1000&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Head of recruitment&lt;/td&gt;
&lt;td&gt;£85k - £110k&lt;/td&gt;
&lt;td&gt;£500 - £600&lt;/td&gt;
&lt;td&gt;£600 - £850&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Technical Sourcer&lt;/td&gt;
&lt;td&gt;£35k - £60k&lt;/td&gt;
&lt;td&gt;£250 - £350&lt;/td&gt;
&lt;td&gt;£350 - £550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Lead Recruiter&lt;/td&gt;
&lt;td&gt;£60k - £85k&lt;/td&gt;
&lt;td&gt;£300 - £500&lt;/td&gt;
&lt;td&gt;£450 - £700&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Recruitment Coordinator Recruiter&lt;/td&gt;
&lt;td&gt;£30k - £50k&lt;/td&gt;
&lt;td&gt;£200 - £250&lt;/td&gt;
&lt;td&gt;£300 - £400&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;</content:encoded></item><item><title><![CDATA[How to run third party applications securely on the same domain?]]></title><description><![CDATA[<p>Have you wondered how companies like Heroku, Netlify and Vercel host and run your applications on a single domain? Is that even safe? What if people try to set/get cookies in other people's sub-domain or root domain? I recently ran into this problem at work, and we ended up going down the rabbit hole of how it all works. I was pleasantly surprised by how it all works, so let's talk about it!</p>]]></description><link>https://blog.hao.dev/how-to-run-third-party-applications-securely-on-the-same-domain</link><guid isPermaLink="false">https://blog.hao.dev/how-to-run-third-party-applications-securely-on-the-same-domain</guid><pubDate>Thu, 30 Dec 2021 16:12:00 GMT</pubDate><content:encoded>&lt;p&gt;Have you wondered how companies like Heroku, Netlify and Vercel host and run your applications on a single domain? Is that even safe? What if people try to set/get cookies in other people&apos;s sub-domain or root domain? I recently ran into this problem at work, and we ended up going down the rabbit hole of how it all works. I was pleasantly surprised by how it all works, so let&apos;s talk about it!&lt;/p&gt;&lt;p&gt;The answer to the above questions is simple, there isn&apos;t a good solution, at least not unless we manually tell each browser that we want our domain handled differently. Just relying on the browsers alone, there is no good way around the fact rogue developers could do harmful things. For example, they could deploy an app on &lt;code class=&quot;language-text&quot;&gt;bad-app.mysite.com&lt;/code&gt; and then try and set cookies on &lt;code class=&quot;language-text&quot;&gt;good-app.mysite.com&lt;/code&gt; created by another developer like you or me. Worse still, there is even nothing stopping rogue devs from setting cookies via JavaScript on the root domain &lt;code class=&quot;language-text&quot;&gt;mysite.com&lt;/code&gt;. So for companies like Netlify and Heroku, hosting different apps on the shared domain seems impossible due to the lack of separation and security concerns, right?&lt;/p&gt;
&lt;p&gt;Luckily, we have this thing called Public Suffix List (PSL). It is a cross-browser initiative that&apos;s created and maintained by Mozilla volunteers. It is essentially a list of domains put together under its unique specification (more on this later). Different browsers would then retrieve this list at various stages and treat these domains differently from others when it comes to setting and getting cookies (Chromium browsers pulls this list during compilation and bake it into the installable app).&lt;/p&gt;
&lt;p&gt;For example, say we want to start hosting third-party apps on subdomains of &lt;code class=&quot;language-text&quot;&gt;mysite.com&lt;/code&gt;. We could submit a change to the PSL Github repository. After the maintainers verified that we are the owner of a given domain, they will merge in our pull request, and then our domain will be added to the official PSL. Going forward, when browsers release new versions they use the PSL with our domain inside, and JavaScript can no longer set cookies on the root domain or other subdomains.&lt;/p&gt;
&lt;h2&gt;But why a manual list?&lt;/h2&gt;
&lt;p&gt;As explained already, there is no good way to determine which domain and sub-domain we should treat differently without help. Here is part of the explanation on the official PSL website:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There was and remains no algorithmic method of finding the highest level at which a domain may be registered for a particular top-level domain (the policies differ with each registry), the only method is to create a list.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;PSL specification&lt;/h2&gt;
&lt;p&gt;Here is the official specification of the PSL:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The list is a set of rules, with one rule per line.&lt;/li&gt;
&lt;li&gt;Each line is only read up to the first whitespace; entire lines can also be commented using &lt;code class=&quot;language-text&quot;&gt;//&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Each line which is not entirely whitespace or begins with a comment contains a rule.&lt;/li&gt;
&lt;li&gt;Each rule lists a public suffix, with the subdomain portions separated by dots (.) as usual. There is no leading dot.&lt;/li&gt;
&lt;li&gt;The wildcard character * (asterisk) matches any valid sequence of characters in a hostname part. (Note: the list uses Unicode, not Punycode forms, and is encoded using UTF-8.) Wildcards are &lt;strong&gt;not&lt;/strong&gt; restricted to appear only in the leftmost position, but they must wildcard an entire label. (I.e. ..foo is a valid rule: *bar.foo is not.)&lt;/li&gt;
&lt;li&gt;Wildcards may only be used to wildcard an entire level. That is, they must be surrounded by dots (or implicit dots, at the beginning of a line).&lt;/li&gt;
&lt;li&gt;If a hostname matches more than one rule in the file, the longest matching rule (the one with the most levels) will be used.&lt;/li&gt;
&lt;li&gt;An exclamation mark (!) at the start of a rule marks an exception to a previous wildcard rule. An exception rule takes priority over any other matching rule.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given we have a basic PSL that looks something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;1. com
2. *.jp             // Hosts in .hokkaido.jp can&apos;t set cookies below level 4...
3. *.hokkaido.jp
4. *.tokyo.jp       // ...except hosts in pref.hokkaido.jp, which can set cookies at level 3.
5. !pref.hokkaido.jp
6. !metro.tokyo.jp&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The browser will treat the above domains as follows:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for foo.com.&lt;/li&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for foo.bar.jp.
Cookies &lt;strong&gt;may not&lt;/strong&gt; be set for bar.jp.&lt;/li&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for foo.bar.hokkaido.jp.
Cookies &lt;strong&gt;may not&lt;/strong&gt; be set for bar.hokkaido.jp.&lt;/li&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for foo.bar.tokyo.jp.
Cookies &lt;strong&gt;may not&lt;/strong&gt; be set for bar.tokyo.jp.&lt;/li&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for pref.hokkaido.jp because the exception overrides the previous rule.&lt;/li&gt;
&lt;li&gt;Cookies &lt;strong&gt;may&lt;/strong&gt; be set for metro.tokyo.jp, because the exception overrides the previous rule.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Real PSL examples&lt;/h2&gt;
&lt;p&gt;So what does submission in PSL looks like?
Here&apos;s the official submission for Netlify and Heroku:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// Netlify : https://www.netlify.com
// Submitted by Jessica Parsons &amp;lt;jessica@netlify.com&gt;
netlify.app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// Heroku : https://www.heroku.com/
// Submitted by Tom Maher &amp;lt;tmaher@heroku.com&gt;
herokuapp.com
herokussl.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are interested, you can find the full list on the PSL website.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In case you need to build a site that hosts apps from strangers, I hope now you know what to do!&lt;/p&gt;
&lt;p&gt;I also want to say that when the web is evolving at a remarkable rate and so much innovation, it is crazy to think there is this list on the internet manually managed by a group of people that determines the behaviours of our browsers. It is nice to see some human power when we are surrounded by machines :)&lt;/p&gt;
&lt;h2&gt;Useful resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://publicsuffix.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://publicsuffix.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://blog.logrocket.com/javascript-developer-guide-browser-cookies&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://blog.logrocket.com/javascript-developer-guide-browser-cookies&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[How to fix Jest and Mock Service Worker integration errors]]></title><description><![CDATA[<p>In a recent project, my team decided to use <a href="https://mswjs.io" target="_blank" rel="nofollow noopener noreferrer">Mock Service Worker</a> (MSW) in our unit tests. It was meant to be a very straightforward change since the documentation for MSW is pretty clear. I encountered a few errors along the way that had me scratching my head. I'm going to document them here, hopefully will help people in a similar situation.</p>]]></description><link>https://blog.hao.dev/how-to-fix-jest-and-mock-service-worker-integration-errors</link><guid isPermaLink="false">https://blog.hao.dev/how-to-fix-jest-and-mock-service-worker-integration-errors</guid><pubDate>Wed, 15 Sep 2021 19:49:00 GMT</pubDate><content:encoded>&lt;p&gt;In a recent project, my team decided to use &lt;a href=&quot;https://mswjs.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Mock Service Worker&lt;/a&gt; (MSW) in our unit tests. It was meant to be a very straightforward change since the documentation for MSW is pretty clear. I encountered a few errors along the way that had me scratching my head. I&apos;m going to document them here, hopefully will help people in a similar situation.&lt;/p&gt;&lt;p&gt;This is what we had before the change:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.config.js&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;preset&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;ts-jest&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;testEnvironment&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;jsdom&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;globals&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&apos;ts-jest&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;tsconfig&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;tsconfig.build.json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Integrating with MSW&lt;/h2&gt;
&lt;p&gt;Following the official guide &lt;a href=&quot;https://mswjs.io/docs/getting-started/integrate/node&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;, I created &lt;code class=&quot;language-text&quot;&gt;server.ts&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;jest.setup.ts&lt;/code&gt; files.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/mocks/server.ts&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; setupServer &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;msw/node&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; handlers &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./handlers&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This configures a request mocking server with the given request handlers.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; server &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setupServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;handlers&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.setup.js&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; server &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./src/mocks/server&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;beforeAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Enable the mocking in tests.&lt;/span&gt;
  server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;afterEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Reset any runtime handlers tests may use.&lt;/span&gt;
  server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;resetHandlers&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;afterAll&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Clean up once the tests are done.&lt;/span&gt;
  server&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;close&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then I just had to reference the setup file in &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// jest.config.js&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;setupFilesAfterEnv&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;./jest.setup.ts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The handler is directly relevant to what is being explained here. But if you are interested, the handlers file looks something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// src/mocks/handlers.ts&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; handlers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  rest&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;https://api.backend.dev/user&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; firstName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;John&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Issue 1 - can&apos;t find module &apos;typescript&apos;&lt;/h2&gt;
&lt;p&gt;After the integration with MSW was done, I saw this error when I tried to run the jest tests. It is very strange because I changed nothing and it suddenly complains &lt;code class=&quot;language-text&quot;&gt;typescript&lt;/code&gt; doesn&apos;t exist.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: Cannot find module &apos;typescript&apos;
Require stack:
- /Users/hao/node_modules/ts-jest/dist/config/config-set.js
- /Users/hao/node_modules/ts-jest/dist/ts-jest-transformer.js
- /Users/hao/node_modules/ts-jest/dist/index.js
- /Users/hao/demo-project/node_modules/jest-util/build/requireOrImportModule.js
- /Users/hao/demo-project/node_modules/jest-util/build/index.js
- /Users/hao/demo-project/node_modules/@jest/core/build/cli/index.js
- /Users/hao/demo-project/node_modules/@jest/core/build/jest.js
- /Users/hao/demo-project/node_modules/jest-cli/build/cli/index.js
- /Users/hao/demo-project/node_modules/jest-cli/bin/jest.js
    at Function.Module._resolveFilename (internal/modules/cjs/loader.js:902:15)
    at Function.Module._load (internal/modules/cjs/loader.js:746:27)
    at Module.require (internal/modules/cjs/loader.js:974:19)
    at require (internal/modules/cjs/helpers.js:92:18)
    at Object.&amp;lt;anonymous&gt; (/Users/hao/node_modules/ts-jest/dist/config/config-set.js:58:20)
    at Module._compile (internal/modules/cjs/loader.js:1085:14)
    at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
    at Module.load (internal/modules/cjs/loader.js:950:32)
    at Function.Module._load (internal/modules/cjs/loader.js:790:14)
    at Module.require (internal/modules/cjs/loader.js:974:19)

error Command failed with exit code 1.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After some digging around, it appears this error is actually complaining about &lt;code class=&quot;language-text&quot;&gt;ts-jest&lt;/code&gt; is missing in the dependencies. See original &lt;a href=&quot;https://stackoverflow.com/questions/63520593/unable-to-run-jest-with-typescript-cannot-find-module-typescript&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;stackoverflow question&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Issue 2 - non-standard JavaScript syntax&lt;/h2&gt;
&lt;p&gt;After installing &lt;code class=&quot;language-text&quot;&gt;ts-jest&lt;/code&gt; as a dev-dependency, I got yet another error. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;● Test suite failed to run

    Jest encountered an unexpected token

    Jest failed to parse a file. This happens e.g. when your code or its dependencies use non-standard JavaScript syntax, or when Jest is not configured to support such syntax.

    Out of the box Jest supports Babel, which will be used to transform your files into valid JS based on your Babel configuration.

    By default &quot;node_modules&quot; folder is ignored by transformers.

    Here&apos;s what you can do:
     • If you are trying to use ECMAScript Modules, see https://jestjs.io/docs/ecmascript-modules for how to enable it.
     • To have some of your &quot;node_modules&quot; files transformed, you can specify a custom &quot;transformIgnorePatterns&quot; in your config.
     • If you need a custom transformation specify a &quot;transform&quot; option in your config.
     • If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the &quot;moduleNameMapper&quot; config option.

    You&apos;ll find more details and examples of these config options in the docs:
    https://jestjs.io/docs/configuration
    For information about custom transformations, see:
    https://jestjs.io/docs/code-transformation

    Details:

    /Users/hao/demo-project/jest.setup.js:1
    ({&quot;Object.&amp;lt;anonymous&gt;&quot;:function(module,exports,require,__dirname,__filename,jest){import { server } from &apos;./src/mocks/server&apos;;
                                                                                     ^^^^^^

    SyntaxError: Cannot use import statement outside a module

      at Runtime.createScriptFromCode (../../node_modules/jest-runtime/build/index.js:1517:14)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This time it is complaining that in &lt;code class=&quot;language-text&quot;&gt;jest.setup.js&lt;/code&gt; file above I used &lt;code class=&quot;language-text&quot;&gt;import&lt;/code&gt;. The fix for this was quite easy, I rename both jest files to &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt; ( &lt;code class=&quot;language-text&quot;&gt;jest.setup.js&lt;/code&gt; → &lt;code class=&quot;language-text&quot;&gt;jest.setup.ts&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;jest.config.js&lt;/code&gt; → &lt;code class=&quot;language-text&quot;&gt;jest.config.ts&lt;/code&gt;).&lt;/p&gt;</content:encoded></item><item><title><![CDATA[DigitalOcean App Platform: how to redirect all requests to index.html for Single Page Applications]]></title><description><![CDATA[<p>If you are not aware, DigitalOcean now offers their own <a href="https://www.digitalocean.com/products/app-platform/" target="_blank" rel="nofollow noopener noreferrer">App Platform</a> which works like <a href="https://www.netlify.com/" target="_blank" rel="nofollow noopener noreferrer">Netlify</a> or <a href="https://vercel.com/" target="_blank" rel="nofollow noopener noreferrer">Vercel</a> if you used those platforms before. If not, essentially you could connect your code repository and then these platforms will take care of the rest e.g. install dependencies, building the project and deploying it somewhere, host it, manage SSL certificates, custom domains, etc.</p>]]></description><link>https://blog.hao.dev/digitalocean-app-platform-how-to-redirect-all-requests-to-index-html-for-single-page-applications</link><guid isPermaLink="false">https://blog.hao.dev/digitalocean-app-platform-how-to-redirect-all-requests-to-index-html-for-single-page-applications</guid><pubDate>Mon, 09 Aug 2021 22:20:00 GMT</pubDate><content:encoded>&lt;p&gt;If you are not aware, DigitalOcean now offers their own &lt;a href=&quot;https://www.digitalocean.com/products/app-platform/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;App Platform&lt;/a&gt; which works like &lt;a href=&quot;https://www.netlify.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Netlify&lt;/a&gt; or &lt;a href=&quot;https://vercel.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Vercel&lt;/a&gt; if you used those platforms before. If not, essentially you could connect your code repository and then these platforms will take care of the rest e.g. install dependencies, building the project and deploying it somewhere, host it, manage SSL certificates, custom domains, etc.&lt;/p&gt;&lt;p&gt;Everything was quite straightforward, the dev experience is noticeably behind that of the competitors which is a little disappointing to see. The free tier is also more stingy in my opinion. But instead of restricting you to Lambda functions (serverless), you could deploy full blow Node.js (or other languages) backend applications. DigitalOcean charges for each &quot;app&quot; instead of by usage which could end up cheaper if you have a moderately popular application.&lt;/p&gt;
&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;If you have used the competitor platforms and tried to deploy single page application, then you would know if a user tries to go to any path that isn&apos;t the root will result in a 404 error.&lt;/p&gt;
&lt;p&gt;Why does this happen?&lt;/p&gt;
&lt;p&gt;When a user tries to navigate directly (e.g. type into URL or refresh) to &lt;code class=&quot;language-text&quot;&gt;domain.com/some-url&lt;/code&gt;, the browser will request &lt;code class=&quot;language-text&quot;&gt;some-url.html&lt;/code&gt; from the server. However, Single Page Application (SPA) runs from &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; file, hence the name. Therefore it does not have the requested file and returns a 404 error.&lt;/p&gt;
&lt;p&gt;How come the router library allows us to go to &lt;code class=&quot;language-text&quot;&gt;domain.com/some-url&lt;/code&gt;? &lt;/p&gt;
&lt;p&gt;Routing libraries such as React Router manipulates the browser history to create the effect of multiple path names and pages are available. &lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;Normally, if we were hosting the SPA ourselves then we&apos;d have to configure Nginx to redirect all traffic to &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt;. But what do we do with DigitalOcean?&lt;/p&gt;
&lt;p&gt;DigitalOcean doesn&apos;t have very clear documentation around this, which could partly be due to the App Platform is still relatively new. I hope this will improve going forward, as this is a pretty fundamental config for any SPA project. After some digging around I found out this is normally done via submitting a config file, which is pretty well documented &lt;a href=&quot;https://docs.digitalocean.com/products/app-platform/references/app-specification-reference/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;. Fortunately, there&apos;s an even easier way to do that now. Navigate to your SPA application under Apps.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/60HVG3Mp3xKnazYdatvmXt/71f0d32116c5a6e65f41f9834e450c67/Screenshot_2021-08-09_at_00.12.13.png&quot; alt=&quot;DigitalOcean App in Nav&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then select the application you wish to make this change to. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/1S7WHXR52sSiJJfPSDycuJ/3a9674de4a1ff0ecebd4f45e0ee12fb8/Screenshot_2021-08-09_at_00.13.09.png&quot; alt=&quot;DigitalOcean App tabs&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now go to the Settings tab, and scroll down to the bottom. There should be a section that says &quot;Custom Pages&quot;. Edit it, input &quot;index.html&quot; and save. Now wait for the redeployment to happen and it should work when you navigate to any paths! 🥳&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/14Z4jh0ryswEi5SfcRB2Pj/6cfc5db0d5e0404011f1ec348c0136fd/Screenshot_2021-08-09_at_00.10.08.png&quot; alt=&quot;DigitalOcean App Settings&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to integrate Auth0 authenticated Frontend applications with API services]]></title><description><![CDATA[<p>Following on from the previous blog post on <a href="https://blog.hao.dev/how-to-setup-single-page-applications-with-auth0-in-local-environment-without-cors-issues" target="_blank" rel="nofollow noopener noreferrer">How to setup Single Page Applications with Auth0 in the local environment without CORS issues</a>, I noticed another area that is very vague in the official docs and thought I'd share my finding in this post. It turns out Auth0 provided everything we needed just didn't tell us how to use it.</p>]]></description><link>https://blog.hao.dev/how-to-integrate-auth0-authenticated-frontend-applications-with-api-services</link><guid isPermaLink="false">https://blog.hao.dev/how-to-integrate-auth0-authenticated-frontend-applications-with-api-services</guid><pubDate>Tue, 03 Aug 2021 21:54:00 GMT</pubDate><content:encoded>&lt;p&gt;Following on from the previous blog post on &lt;a href=&quot;https://blog.hao.dev/how-to-setup-single-page-applications-with-auth0-in-local-environment-without-cors-issues&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;How to setup Single Page Applications with Auth0 in the local environment without CORS issues&lt;/a&gt;, I noticed another area that is very vague in the official docs and thought I&apos;d share my finding in this post. It turns out Auth0 provided everything we needed just didn&apos;t tell us how to use it.&lt;/p&gt;&lt;p&gt;This blog post assumes you have a working Frontend app already. If you are not quite there yet, be sure to check out &lt;a href=&quot;/how-to-setup-single-page-applications-with-auth0-in-local-environment-without-cors-issues&quot;&gt;How to setup Single Page Applications with Auth0 in local environment without CORS issues&lt;/a&gt; if you want some tips!&lt;/p&gt;
&lt;h2&gt;Auth0&apos;s user object&lt;/h2&gt;
&lt;p&gt;If you are not aware already, on the Frontend side it is possible to request a user profile from Auth0 and it will return something similar to the following. Assuming, of course, you&apos;ve got the authentication working already.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hao@email.com&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;email_verified&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;family_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dev&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;given_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;locale&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;en-GB&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao Dev&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Nickname&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;picture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://image.com/fake-profile-pic.png&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;google-oauth2|1924810923701923&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;updated_at&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2021-07-29T17:40:14.223Z&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The important thing to note here is the &lt;code class=&quot;language-text&quot;&gt;sub&lt;/code&gt; property, this is the equivalent of something like &lt;code class=&quot;language-text&quot;&gt;user.id&lt;/code&gt; we typically use to refer to a unique user in our database. If we want to store custom data about this user or user inputted data then we would save them against the &lt;code class=&quot;language-text&quot;&gt;sub&lt;/code&gt; value and later we can retrieve it using the same thing.&lt;/p&gt;
&lt;h2&gt;Attach access token to API requests&lt;/h2&gt;
&lt;p&gt;According to &lt;a href=&quot;https://auth0.com/docs/flows/call-your-api-using-the-client-credentials-flow&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Call Your API Using the Client Credentials Flow&lt;/a&gt; section of the doc, we need to attach the access token as a bearer token in all requests headers. Which makes sense as this will allow the Backend to authenticate and verify the user without blindly trusting what the Frontend says.&lt;/p&gt;
&lt;p&gt;Here&apos;s the node example given in the above documentation:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; axios &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;axios&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;method&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;GET&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://myapi.com/api&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token string-property property&quot;&gt;&apos;content-type&apos;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;application/json&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;authorization&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Bearer ACCESS_TOKEN&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

axios&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;response&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Receiving access token on Backend&lt;/h2&gt;
&lt;p&gt;On the Backend, we will need to intercept all requests that require authentication. Luckily, Auth0 has made this part very easy to find (see screenshot below).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/34RBlZniBcS9YyRyszc0ca/d7b56cc5e97541263d9fbc0d214cd036/Screenshot_2021-08-03_at_22.17.16.png&quot; alt=&quot;Auth0 API instructions&quot;&gt;&lt;/p&gt;
&lt;p&gt;The code example given on the page is unique to each API, here is an example to illustrate to anyone who hasn&apos;t set up an API yet.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; express &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;express&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;express&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jwt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;express-jwt&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jwks &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;jwks-rsa&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; port &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8080&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; jwtCheck &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;jwt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; jwks&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;expressJwtSecret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;cache&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;rateLimit&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;jwksRequestsPerMinute&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
          &lt;span class=&quot;token literal-property property&quot;&gt;jwksUri&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://example-api-dev.eu.auth0.com/.well-known/jwks.json&apos;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;audience&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://an-example.com&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;issuer&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;https://example-api-dev.eu.auth0.com/&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;algorithms&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;RS256&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtCheck&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/authorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Secured Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;port&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In this example, it shows us that we need to use two libraries here &lt;code class=&quot;language-text&quot;&gt;express-jwt&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;jwks-rsa&lt;/code&gt; to verify the access/bearer token passed from the Frontend&apos;s side. Then using &lt;code class=&quot;language-text&quot;&gt;jwtCheck()&lt;/code&gt; function as a middleware, the request will only reach &lt;code class=&quot;language-text&quot;&gt;app.get(&apos;/authorized&apos;)&lt;/code&gt; if the JWT token passes the authentication check.&lt;/p&gt;
&lt;p&gt;Now we have used the access token that came as part of the request and verified it with Auth0 and made sure it is valid and haven&apos;t been tampered with.&lt;/p&gt;
&lt;h2&gt;Getting unique identifier on Backend&lt;/h2&gt;
&lt;p&gt;Apparently, &lt;code class=&quot;language-text&quot;&gt;jwtCheck&lt;/code&gt; verification automatically populates &lt;code class=&quot;language-text&quot;&gt;req.user&lt;/code&gt; if everything is successful. After checking out &lt;code class=&quot;language-text&quot;&gt;express-jwt&lt;/code&gt; documentation, it seems that it is doing all that work for us.&lt;/p&gt;
&lt;p&gt;It is very confusing because that isn&apos;t made clear anyway in the Auth0 docs and I went through a lot of them. I was lost and confused until I saw &lt;a href=&quot;https://github.com/auth0-samples/auth0-pnp-exampleco-timesheets/blob/master/timesheets-api/node/server.js#L63&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this line&lt;/a&gt; in one of the example codebases which helped me to figure it out.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtCheck&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/authorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// if we can reach here, then req.user is already populated&lt;/span&gt;
        console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;req&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Secured Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The console log will print out the same user object shown on the Frontend side.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;email&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hao@email.com&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;email_verified&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;family_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Dev&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;given_name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;locale&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;en-GB&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao Dev&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;nickname&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Nickname&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;picture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://image.com/fake-profile-pic.png&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;sub&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;google-oauth2|1924810923701923&quot;&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;updated_at&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2021-07-29T17:40:14.223Z&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Keeping some endpoints open&lt;/h2&gt;
&lt;p&gt;Is it possible to not use &lt;code class=&quot;language-text&quot;&gt;app.use(jwtCheck);&lt;/code&gt; and lock down all of the endpoints? Yes, as far as I am aware there are a couple of ways to achieve this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Authenticate each endpoint/route individually&lt;/li&gt;
&lt;li&gt;Define &lt;code class=&quot;language-text&quot;&gt;app.use(jwtCheck);&lt;/code&gt; after the endpoints that don&apos;t require authentication&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Although in many of Auth0&apos;s examples I read that it is possible to define JWT verification on each endpoint, I never actually got the first method working myself. The following snippet is supposed to work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ❌ didn&apos;t work for me&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/authorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jwtCheck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Secured Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/unauthorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Public Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/another-authorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jwtCheck&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Another Secured Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the end, I went with the second approach, which works for me with no problem. It is unfortunate since the first solution would have been a lot neater. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ✅ worked for me&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/unauthorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Public Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jwtCheck&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;/authorized&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;req&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; res&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    res&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;Secured Resource&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I hope this information was useful, I was stuck on this for a long time because I couldn&apos;t find any information on it. &lt;/p&gt;
&lt;p&gt;PS: If you want to know more about authorisation checks (e.g. use Auth0&apos;s permission system) check out &lt;a href=&quot;https://auth0.com/docs/architecture-scenarios/server-api/part-2#configure-the-scopes&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Server + API: Auth0 Configuration&lt;/a&gt; and &lt;a href=&quot;https://auth0.com/docs/tokens/access-tokens/validate-access-tokens&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Validate Access Tokens&lt;/a&gt;. Although they don&apos;t give you the code you need, it is a good place to start.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to setup Single Page Applications with Auth0 in local environment without CORS issues]]></title><description><![CDATA[<p>I used Auth0 in a Single Page Application (SPA) side project recently, because I didn't want to spend the time to build all the auth related features e.g. password reset, sign up, etc. Surprisingly it was extremely painful to get it working locally due to HTTPS, CORS and localhost issues. In this blog post, I'll be walking my final setup with all of that working locally.</p>]]></description><link>https://blog.hao.dev/how-to-setup-single-page-applications-with-auth0-in-local-environment-without-cors-issues</link><guid isPermaLink="false">https://blog.hao.dev/how-to-setup-single-page-applications-with-auth0-in-local-environment-without-cors-issues</guid><pubDate>Sat, 31 Jul 2021 14:52:00 GMT</pubDate><content:encoded>&lt;p&gt;I used Auth0 in a Single Page Application (SPA) side project recently, because I didn&apos;t want to spend the time to build all the auth related features e.g. password reset, sign up, etc. Surprisingly it was extremely painful to get it working locally due to HTTPS, CORS and localhost issues. In this blog post, I&apos;ll be walking my final setup with all of that working locally.&lt;/p&gt;&lt;p&gt;You might also want to check out &lt;a href=&quot;/how-to-integrate-auth0-authenticated-frontend-applications-with-api-services&quot;&gt;How to integrate Auth0 authenticated Frontend applications with API services&lt;/a&gt; after are done setting up the Frontend app.&lt;/p&gt;
&lt;h2&gt;Simple app architecture&lt;/h2&gt;
&lt;p&gt;I am going for simple and very standard app architecture in my project. I wanted to separate my frontend from the backend because I didn&apos;t want Server Sider Rendering (SSR). When the user visits my web app, I want the frontend to call the backend for any data.&lt;/p&gt;
&lt;p&gt;In this case, I&apos;m using React (create-react-app) on the frontend and Node express server on the backend.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/2FSyYcVvpB163FnYwoVAhw/e9de20c370e6bd27f467162df56952dc/Screenshot_2021-07-30_at_18.21.09.png&quot; alt=&quot;Simple SPA architecture diagram&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Simple app with Auth0&lt;/h2&gt;
&lt;p&gt;Here&apos;s an updated diagram with Auth0 added to the picture. Now the flow is a little more complex. When someone visits my web app, I now want to redirect them to Auth0 for sign up or sign in before returning to my app to continue with the user journey (steps 1, 2 and 3 in the diagram).&lt;/p&gt;
&lt;p&gt;When the user wants to edit any app data, the frontend will attach the bearer token from Auth0 and send it with the request to the backend (steps 4 - 5). The backend will then verify the user is who they claim they are by calling Auth0 with that bearer token, then return the data to the frontend if all is good (steps 6 - 8). Finally, the front end will display the updated UI on the browser (step 9).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.contentful.com/zsjhz5fgria0/165fC33nFxO3yX0atGkyD2/b190cc794ed69a1416845862e97f48f9/Screenshot_2021-07-30_at_18.21.21.png&quot; alt=&quot;SPA with Auth0 architecture diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;This flow is more complex compared to what we started with, but it is still pretty standard. Any sort of auth service or Single Sign On (SSO) will involve a similar process. Now on to the more complex part - local setup.&lt;/p&gt;
&lt;h2&gt;Auth0 in the local environment&lt;/h2&gt;
&lt;p&gt;While Auth0 has dedicated a &lt;a href=&quot;https://auth0.com/docs/applications/work-with-auth0-locally&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Test Applications Locally&lt;/a&gt; section in their website that focuses on local app testing, unfortunately, it is pretty basic as far as documentation goes. It is tough to pinpoint which type of app architecture they are referring to. As far as I understand this section seems to be for SSR apps, not SPA. There&apos;s also a section on &lt;a href=&quot;https://auth0.com/docs/libraries/secure-local-development&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;HTTPS in Development&lt;/a&gt; (I&apos;ll explain why we need HTTPS later), which I&apos;m certain isn&apos;t for the architecture in our diagram above.&lt;/p&gt;
&lt;p&gt;So long story short, I didn&apos;t find any good guidance on Auth0&apos;s website so I had to forge my own.&lt;/p&gt;
&lt;h2&gt;We need HTTPS everywhere&lt;/h2&gt;
&lt;p&gt;We cannot authenticate with Auth0 if our frontend is running in HTTP mode. If you do, then you&apos;ll get the following error message.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Error: auth0-spa-js must run on a secure origin.
See https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md&lt;span class=&quot;token comment&quot;&gt;#why-do-i-get-auth0-spa-js-must-run-on-a-secure-origin for more information.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Luckily, it is pretty easy to get around this if you are using create react app, it is as simple as &lt;code class=&quot;language-text&quot;&gt;HTTPS=true&lt;/code&gt; to your start command e.g. &lt;code class=&quot;language-text&quot;&gt;HTTPS=true npm start&lt;/code&gt;. See more about it &lt;a href=&quot;https://create-react-app.dev/docs/using-https-in-development/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Doing this fixes the issue with Auth0, but now Chrome will complain that our HTTPS frontend is now sending non-secure requests (HTTP) to the backend. So next we will need to run the backend in HTTPS mode. To do this with Express, we will need to generate some certificates, I recommend using &lt;a href=&quot;https://github.com/FiloSottile/mkcert&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;mkcert&lt;/a&gt;. This will output two files a key and a certificate, which we will both need to run the Express server in HTTPS mode. You will then do something like the following.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; fs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;fs&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; https &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; privateKey &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../../key.pem&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; certificate &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; fs&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;readFileSync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;../../cert.pem&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;utf8&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; credentials &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;key&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; privateKey&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;cert&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; certificate &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; httpsServer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; https&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createServer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;credentials&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

httpsServer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;PORT&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;8000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, now we have these certificates we might as well use them on the frontend. Create react app to the rescue again! All we need to do is include the following in our &lt;code class=&quot;language-text&quot;&gt;.env&lt;/code&gt; file and &lt;code class=&quot;language-text&quot;&gt;react-scripts&lt;/code&gt; will pick it up during development mode automatically.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token constant&quot;&gt;SSL_CRT_FILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;cert&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem
&lt;span class=&quot;token constant&quot;&gt;SSL_KEY_FILE&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;pem&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;We can&apos;t be on localhost&lt;/h3&gt;
&lt;p&gt;To do step 5 in the second diagram, we need to use &lt;code class=&quot;language-text&quot;&gt;getAccessTokenSilently()&lt;/code&gt; (again I&apos;m using React, your mileage may vary) to extract the bearer token we need to pass to our backend. This, however, will trigger a consent check (even though this is a first-party application!) and if we are running on &lt;code class=&quot;language-text&quot;&gt;[localhost](http://localhost)&lt;/code&gt; it will fail silently. You can read more about it on the &lt;a href=&quot;https://auth0.com/docs/authorization/user-consent-and-third-party-applications&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;User Consent and Third-Party Applications&lt;/a&gt; page.&lt;/p&gt;
&lt;p&gt;If you are on mac or linux then edit &lt;code class=&quot;language-text&quot;&gt;/etc/host&lt;/code&gt;, if you are windows it is &lt;code class=&quot;language-text&quot;&gt;C:\Windows\System32\Drivers\etc\hosts&lt;/code&gt;. You could replace &lt;code class=&quot;language-text&quot;&gt;[localhost](http://localhost)&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;[example.site](http://example.site)&lt;/code&gt; but I prefer to keep both since I work on codebases that assume &lt;code class=&quot;language-text&quot;&gt;localhost&lt;/code&gt; as well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token number&quot;&gt;127.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.0&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;.1&lt;/span&gt;  localhost example&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;site&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;CORS, CORS, CORS&lt;/h3&gt;
&lt;p&gt;CORS on the local environment is so frustrating. If you follow the instructions above, then hopefully in your Express server you can just add the following and everything should work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alternatively, consider adding some whitelist logic.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; whitelist &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ORIGIN_WHITELIST_URL&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://production.site&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; corsOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; any&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; any&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;whitelist&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;includes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;origin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;origin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Not allowed by CORS&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;corsOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If it still doesn&apos;t work, try allowing any origin. Of course, this isn&apos;t secure, so come back to this later and bulletproof it for production.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; cors &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cors&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; corsOptions &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;origin&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;*&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;use&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cors&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;corsOptions&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Alternative service to auth0&lt;/h2&gt;
&lt;p&gt;If you are thinking of using Auth0, I came across another service called &lt;a href=&quot;https://magic.link&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Magic&lt;/a&gt; which seems to offer a much better dev experience as a whole. Their local environment setup seems too good to be true, check it out &lt;a href=&quot;https://magic.link/docs/client-sdk/web/get-started&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;. However, the reason I went with Auth0 at the end is because of their generous free tier which allows up to 1000 active users. Magic in comparison charges based on the number of user logins and there is no free allowance.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[State of Yarn 2 (Berry) in 2021]]></title><description><![CDATA[<p>Yarn 2, aka Berry, was announced back in April 2019, it was a pretty big deal at the time. I've moved 2 clients since and still, no one is talking about it. I got curious recently and did some research about this topic. In this post, we'll see what is the deal with Yarn 2, and what the adoption is like after 2 years.</p>]]></description><link>https://blog.hao.dev/state-of-yarn-2-berry-in-2021</link><guid isPermaLink="false">https://blog.hao.dev/state-of-yarn-2-berry-in-2021</guid><pubDate>Tue, 20 Jul 2021 20:20:00 GMT</pubDate><content:encoded>&lt;p&gt;Yarn 2, aka Berry, was announced back in April 2019, it was a pretty big deal at the time. I&apos;ve moved 2 clients since and still, no one is talking about it. I got curious recently and did some research about this topic. In this post, we&apos;ll see what is the deal with Yarn 2, and what the adoption is like after 2 years.&lt;/p&gt;&lt;h2&gt;What was the biggest change in Yarn 2?&lt;/h2&gt;
&lt;p&gt;The creator and maintainers of Yarn identified an issue we have in the JavaScript community and found a brilliant solution to address it. The solution is called Plug&apos;n&apos;Play (PnP).  Surprisingly, the PnP feature exists in Yarn 1. The feature was unveiled back in September 2018, almost a year before Yarn 2&apos;s announcement. However, it was turned off by default due to incompatibility issues with well known NPM modules. As you can imagine almost no one used this feature for this reason. Then when the Yarn team released Yarn 2, PnP was on by default and can&apos;t be turned off unless you use some highly experimental workaround. I guess they were hoping this will push up the adoption rate.&lt;/p&gt;
&lt;h2&gt;But why did the maintainers want everyone to use PnP so bad?&lt;/h2&gt;
&lt;p&gt;Yarn team discovered that the current way of maintaining JavaScript dependencies is extremely inefficient and there is a lot of room for improvement. For example, 70% of the &lt;code class=&quot;language-text&quot;&gt;Yarn install&lt;/code&gt; time is used to generate &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt;. Sadly, without drastic changes, there is no way for package managers to optimise anything. This means the problems are not going to get better over time without intervention.&lt;/p&gt;
&lt;p&gt;If you are interested here are a few other ways why the &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; design is wasteful and impractical see &lt;a href=&quot;https://yarnpkg.com/features/pnp#the-node_modules-problem&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Yarn: The Node Module Problem&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;So, how does PnP fix these issues?&lt;/h2&gt;
&lt;p&gt;PnP generates a &lt;code class=&quot;language-text&quot;&gt;.pnp.cjs&lt;/code&gt; file that points to packages in local disk location. Instead of Node looking for a package in a folder, Yarn will tell Node where to find the packages instead. Since now Yarn only needs to generate a single file instead of creating a &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; folder of modules during install. It is much faster and has way fewer I/O operations, meaning a massive performance boost!&lt;/p&gt;
&lt;p&gt;By committing &lt;code class=&quot;language-text&quot;&gt;.npn.cjs&lt;/code&gt; file it is even possible to avoid running &lt;code class=&quot;language-text&quot;&gt;yarn install&lt;/code&gt; (hence the name Plug&apos;n&apos;Play). Node apps will also launch much faster without Node iterating over files to find what it needs. Just remember back when Windows XP supported USB devices without the user installing any drivers, this was meant to be a game-changer. &lt;/p&gt;
&lt;h2&gt;If Yarn 2&apos;s PnP feature is so awesome then why so much resistance?&lt;/h2&gt;
&lt;p&gt;As mentioned above the main issue is around the breaking changes and requires existing packages to be updated by their respective maintainers to be compatible with PnP. This caused further discussions around if Yarn 2 should be released as a separate package manager altogether under a different name e.g. Berry. It is obvious now looking back, the suggestion wasn&apos;t adopted.&lt;/p&gt;
&lt;p&gt;In the early stages, people who tried Yarn 2 reported a lot of errors when they tried to upgrade. A lot of tools and libraries people rely on stopped working or experienced issues after the upgrade. Yarn 2 offers workarounds such as adding  &lt;code class=&quot;language-text&quot;&gt;pnpMode: loose&lt;/code&gt; into &lt;code class=&quot;language-text&quot;&gt;.yarnrc.yml&lt;/code&gt;  to disable PnP. But even this was a highly experimental feature at the time. &lt;/p&gt;
&lt;p&gt;Unfortunately, not everyone is on board with this change. Facebook and Twitter both came out and said they plan on staying with Yarn 1. There must be a lot of other smaller companies which also didn&apos;t want to spend ages upgrade to Yarn 2 and risk their product stability and product roadmap.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;The Yarn maintainers wanted to push the brilliant PnP feature, which has been in Yarn 1 for ages, for wider adoption. To achieve that goal, they wanted to force developers to start building and using PnP (reminds me of how Apple forces people to move to newer I/O ports).&lt;/p&gt;
&lt;p&gt;The fact it breaks compatibility with a lot of projects caused concerns within the community. Well-known names in the JavaScript community opened stated on Twitter that they or their team wouldn&apos;t be upgrading to Yarn 2. This probably further hindered the adoption rate. But the root cause is the lack of easy migration between the two versions since a lot of the issues lies with the NPM package maintainers who needs to spend time supporting Plug&apos;n&apos;Play. People just didn&apos;t want to run the risk of upgrading and having that one library not working for them.&lt;/p&gt;
&lt;p&gt;If you are familiar with Python 2 and 3 drama over the past ten plus years then you&apos;ll find the story pretty similar here. Python 3 breaking changes were introduced without real ways for developers to bridge the gap. So for years, Python devs struggled with the two major versions. Unable to move to the new version completed and unable to use newer libraries that only support v3. From my own experience, it seems like that&apos;s been getting better.&lt;/p&gt;
&lt;h2&gt;State of Yarn 2 in 2021&lt;/h2&gt;
&lt;p&gt;In all the companies I&apos;ve worked at over the past 3 years, only one company looked into adopting Yarn 2. As far as I&apos;m aware, other than a presentation showcasing its benefits, that didn&apos;t go anywhere. I almost can&apos;t find any discussions online regarding Yarn 2. I don&apos;t think Yarn 2 is dead, but compared to Yarn 1 the adoption rate is much slower.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Should you use Docker for local development?]]></title><description><![CDATA[<p>I've worked for many companies over the years since I'm in the field of consulting/contracting. It allowed me to see how teams do things differently and the pros/cons of each approach. In this blog, I want to share some of my observations on using Docker for local development.</p>]]></description><link>https://blog.hao.dev/should-you-use-docker-for-local-development</link><guid isPermaLink="false">https://blog.hao.dev/should-you-use-docker-for-local-development</guid><pubDate>Mon, 12 Jul 2021 10:52:00 GMT</pubDate><content:encoded>&lt;p&gt;I&apos;ve worked for many companies over the years since I&apos;m in the field of consulting/contracting. It allowed me to see how teams do things differently and the pros/cons of each approach. In this blog, I want to share some of my observations on using Docker for local development.&lt;/p&gt;&lt;p&gt;Firstly, I just want to put it out there that I think Docker is great and helps to make deployments much more reliable by providing a consistent environment to build and run apps. The ability to use the same environment in CI and Production is amazing, it helps to catch issues that otherwise are missed. &lt;/p&gt;
&lt;p&gt;So do I think teams should use Docker for local development? &lt;/p&gt;
&lt;p&gt;Unfortunately, no, I don&apos;t believe most teams should be using Docker for development and they probably should move away from it as soon as possible.&lt;/p&gt;
&lt;h2&gt;1. Slow performance&lt;/h2&gt;
&lt;p&gt;When I used Docker to run an application, I never thought &quot;oh yeah, this makes the app run much faster&quot;. Nope, it is always the opposite - a lot slower! Running a decent sized application inside docker will be noticeably slower since docker containers have limited resources than the Operating System on your machine. I don&apos;t doubt it is nice for someone else to write all the config/script to spin up everything, while you just kick back and relax as one command bring everything online.&lt;/p&gt;
&lt;p&gt;As a developer, I think we all understand the benefits and drawbacks of too much presumption. What makes your life easier also hides aways some customisation options that are useful tools in our toolkit. As a software engineer, I believe the more important thing here is to be intentional. Using something for the sake of using it isn&apos;t a good reason, neither is using Docker when it isn&apos;t required. Why trade something like performance, slower feedback loop and increase complexity for the percieved benefit of Docker making things &quot;easier&quot;?&lt;/p&gt;
&lt;h2&gt;2. Additional friction and another point of failure&lt;/h2&gt;
&lt;p&gt;I find any codebase that uses Docker a lot less intuitive to see what&apos;s going on. It is the same as calling custom script as part of &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;yarn start&lt;/code&gt;. It is another step in the process, and this step is abstracted away for better or for worse. For sure do it if your codebase is unique in that way and requires tens of lines of node scripts to set up and run (in case of custom scripts). But most of the time that just means something isn&apos;t right about the codebase. I&apos;d look to solve the root problem instead of bolt-on something that just fixes the symptoms. Similarly, I think it&apos;s the same with using Docker for local development. Most of the time, the repo or app architecture shouldn&apos;t be that complex where Docker is necessary.&lt;/p&gt;
&lt;p&gt;Docker normally stays out of the way, but like everything else, it also requires maintenance and sometimes causes problems too. I can recall times where I just wanted to run an app quickly to check something but instead had to go out of my and spend time on debugging docker issues. Maybe someone changed the Dockerfile and you just had to pull changes and rebuild the docker image. Or maybe it&apos;s something more complex, now the question becomes how far down the rabbit hole are you willing to go?&lt;/p&gt;
&lt;p&gt;The fact all the Dockerfile used to spin up a basic node app is never the same is almost comical, somehow we all seem to want to use different base images, different library installations, different mount directories, etc than everyone else. The lack of consistency and standard, makes reading each Dockerfile is just as hard as the next one. Of course I am just as guilty and as anyone when it comes to this. But I&apos;d like to argue we are never set up for success. How often do you find Dockerfile guideline documentation in a company? I don&apos;t even believe there is a consistent way agreed by the wider community.&lt;/p&gt;
&lt;p&gt;Then there is the release process for Docker images, some teams have dedicated CI whilst others just let you release to Dockerhub (assuming you don&apos;t need to chase people for permission). I&apos;ve yet to see this managed in a way that screamed consistency or good practice. All this is often decided by one person, often new to the company and want to set a  &quot;standard way of working&quot; that ends up introduces more disagreements. Normally DevOps teams create the initial Dockerfiles, then they don&apos;t have the time to maintain everything and teams ignore the Dockerfiles until something breaks.&lt;/p&gt;
&lt;p&gt;I&apos;m a big fan of simple is better or KISS for those who like acronyms. I just think sometimes people having local issues isn&apos;t the way to introduce a solution that makes everyone&apos;s life a bit harder. Many of these issues can be resolved by being better at pairing and writing better docs.&lt;/p&gt;
&lt;h2&gt;3. Difficult to debug&lt;/h2&gt;
&lt;p&gt;Dockerfiles are often written to be used in the Production environment, so debugging is turned off by default. Maybe we can change the Dockerfile, build it locally then use that image to run the app with debugging turned on? Doesn&apos;t that sound massively exhausting? But yeah, I admit that&apos;s a solution. How about in situations where the repo doesn&apos;t include the Dockerfile it needs, instead it pulls Production docker images to run the app locally. It turns out if you want to modify the docker image, you need to go on a treasure hunt and find it in another repo. I&apos;ve even seen developers host company docker images&apos; code on their own Github account. Maybe these are extreme examples, but they all happened to me.&lt;/p&gt;
&lt;p&gt;My point is sometimes we need to run the app slightly differently to normal production build, especially when debugging issues or trying to narrow down a problem. It isn&apos;t the best time to be messing around with Dockerfiles and docker images.&lt;/p&gt;
&lt;h2&gt;But how can I easily spin up the whole app without docker?&lt;/h2&gt;
&lt;p&gt;The right answer is we don&apos;t. Let&apos;s skip the argument of why monolith is bad, I assume most people agree with the fact that it should be avoided at all cost. If you are working on a tiny slice of the codebase, then the ideal scenario is to only spin up that slice (in a micro-frontend setup), run that slice locally and proxy everything else to your dev or staging environment. Since we don&apos;t care about the rest of the application in this scenario. If there is a concern that changing this slice might break another app, then perhaps there should be more integration in CI, have more decoupled apps or more contract testing between apps.&lt;/p&gt;
&lt;p&gt;If the feature requires working on multiple apps to deliver. I&apos;d argue each app should be worked on one at a time, changes deployed to Production under a feature flag or released in a way that it doesn&apos;t introduce a breaking change. Why? Assuming each app has separate CI/CD (if it doesn&apos;t then it should, it&apos;s 2021, there are no good reasons to group apps and do big bang releases), if there are inter-dependencies how can you guarantee the release to Production doesn&apos;t cause a problem if one app got released before another. Implement backwards compatible change and then deprecate later is a much better approach for situations like this. Unfortunately, many teams are either not working in codebases that allow them to do this, or the company culture is to do edit multiple apps and release in a big bang fashion.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I think the goal of local development is never to code in the Production runtime environment. We already have CI and CD pipeline to detect and spot any errors caused by the difference in setup or environment configuration. Not to mention Staging or QA environments dedicated to catching any issues that are otherwise missed by automated tests. Although it sounds useful, I&apos;m not convinced we should run applications through Docker locally. &lt;/p&gt;
&lt;p&gt;As developers, we need and are entitled to fast feedback cycles for either TDD or to write iterative code. It is an incredibly stressful feeling to be constantly taken out of the zone because the computer becomes the bottleneck and a liability. All the companies I work for say they want their developer to be happy. They understand happy developers are more productive. In my opinion, using Docker for local development isn&apos;t the path that leads to developer happiness.&lt;/p&gt;
&lt;p&gt;Just a short disclaimer, I&apos;m not saying don&apos;t ever use Docker for local development. If there are serious benefits and everything has been weighted and Docker came out on top, then sure go for it! I just believe it shouldn&apos;t be used unless there are really good reasons, and definitely shouldn&apos;t be the default option of running applications locally.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Interview notes on React Reconciliation (Virtual DOM)]]></title><description><![CDATA[<p>It's been a while since I had to recall the reasons why React uses Virtual DOM. I was recently asked this in an interview, and I did an okay job explain it but not to the extent I wanted. So I spent some time re-reading the <a href="https://reactjs.org/docs/reconciliation.html" target="_blank" rel="nofollow noopener noreferrer">official documentation</a> and wrote down some notes. I hope these will be helpful for anyone preparing for a React interview. Also, I have called out the vital part of each section to make it easier to see the key points.</p>]]></description><link>https://blog.hao.dev/interview-notes-on-react-reconciliation-virtual-dom</link><guid isPermaLink="false">https://blog.hao.dev/interview-notes-on-react-reconciliation-virtual-dom</guid><pubDate>Thu, 20 May 2021 14:36:00 GMT</pubDate><content:encoded>&lt;p&gt;It&apos;s been a while since I had to recall the reasons why React uses Virtual DOM. I was recently asked this in an interview, and I did an okay job explain it but not to the extent I wanted. So I spent some time re-reading the &lt;a href=&quot;https://reactjs.org/docs/reconciliation.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt; and wrote down some notes. I hope these will be helpful for anyone preparing for a React interview. Also, I have called out the vital part of each section to make it easier to see the key points.&lt;/p&gt;&lt;h2&gt;What is it?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Virtual DOM is the virtual representation of all the elements on the page in Javascript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Virtual DOM, like the name suggests, is a virtual JavaScript representation of the actual page DOM. All the elements which exist on the page also exist in this virtual representation. When React code detects a change, it will perform a differentiation (AKA diff) by comparing the before and after virtual DOM objects. The diff algorithm will compute a list of DOM manipulations that it deems necessary to bring the actual DOM in line with the virtual DOM. As a result, the user will see the page changing when they interact with it (or changing in the background automatically if that&apos;s the intended behaviour like a stock ticker site).&lt;/p&gt;
&lt;h2&gt;Why virtual DOM?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;React uses Virtual DOM because it makes page updates more performant when there are many elements on the page. Since it could figure out which nodes need to be updated, then go and do that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The virtual DOM makes it easier to handle changes on the page, displaying a basic page layout or handling complex DOM changes. It only re-renders the part of the DOM that changed (we&apos;ll cover how in the Diff Algorithm section below) whilst leaving the rest intact. Therefore it ensures page element update speed even when there are tens of thousands of elements on the page since only a tiny subset of the total number of elements in the DOM requires updating.&lt;/p&gt;
&lt;p&gt;Virtual DOM allows developers to focus on what needs to happen rather than how it happens. By handling DOM changes for the developer automatically, it makes writing web applications much faster.&lt;/p&gt;
&lt;h2&gt;Virtual DOM isn&apos;t a silver bullet&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Like many solutions, React Virtual DOM shines in some scenarios, specifically complex DOM tree structures that are deep and wide.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It&apos;s worth noting that React&apos;s virtual DOM only has an edge in performance if the DOM tree structure is both deep and wide. Virtual DOM isn&apos;t faster 100% of the time.&lt;/p&gt;
&lt;p&gt;In small applications written in vanilla, JavaScript could find an element with &lt;code class=&quot;language-text&quot;&gt;getElementById&lt;/code&gt; and update it with much less time and resource. However, the vanilla way is an old school way of writing web applications, mainly because it isn&apos;t scalable in larger projects. For one, it is not a realistic expectation that developers will assign every DOM element an &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; when working in large applications. If we don&apos;t do this, then finding a part becomes more computationally expensive. Not to mention the development overhead with assigning and recalling &lt;code class=&quot;language-text&quot;&gt;id&lt;/code&gt; for each element on the page.&lt;/p&gt;
&lt;p&gt;Angular also abstracted away from the complications of manual DOM updates but doesn&apos;t utilise Virtual DOM. In comparison, in smaller and more straightforward applications, React&apos;s Virtual DOM wouldn&apos;t contribute to much performance gain and might negatively impact it. Instead, it shines in more significant codebases.&lt;/p&gt;
&lt;h2&gt;Tradeoffs&lt;/h2&gt;
&lt;p&gt;The React team highlighted two main problems:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Need to ensure element types (e.g. div to a span ) only changes when necessary since React will blindly reconstruct all the nodes under that element.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Altering a component&apos;s type might be very costly, especially if they have identical nested nodes since React virtual DOM will reconstruct all the nodes under the changed element.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Must provide keys to all the list item to ensure the O(n) performance.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Using non-unique and reliable keys could result in poor performance where the O(n) performance isn&apos;t guaranteed. See in the next section on why this is the case.&lt;/p&gt;
&lt;h2&gt;Diff Algorithm&lt;/h2&gt;
&lt;p&gt;Imagine the virtual DOM as a tree structure. The algorithm will start from the root node then traverse through the whole tree. React checks for two things to see if an element has changed:
Is the element type different?
If the type has changed, then tear down and reconstructive all of the nodes under this element. If nothing has changed, then continue.
If the element properties/attributes changed?
When a component updates, the instances remain the same so that the state is maintained across renders.&lt;/p&gt;
&lt;p&gt;Update new props on the underlying component instance, and call render() to regenerate the sub-tree&lt;/p&gt;
&lt;h2&gt;FAQ&lt;/h2&gt;
&lt;h3&gt;Why are keys used in React lists?&lt;/h3&gt;
&lt;p&gt;Keys can reduce recursive elements lists to only require a single parsing &lt;code class=&quot;language-text&quot;&gt;O(n)&lt;/code&gt; to update the whole tree. Whereas without using keys, the diff and update process is more computationally expensive &lt;code class=&quot;language-text&quot;&gt;O(n^3)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By default, React will assign an item&apos;s list index as the key. However, this only partially solves the problem, it works when the list remains the same, and just one element&apos;s properties are updated. When elements get added/removed, their indexes change to end up with a similar issue.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;ul&gt;
  &amp;lt;li&gt;Duke&amp;lt;/li&gt;
  &amp;lt;li&gt;Villanova&amp;lt;/li&gt;
&amp;lt;/ul&gt;

&amp;lt;ul&gt;
  &amp;lt;li&gt;Connecticut&amp;lt;/li&gt;
  &amp;lt;li&gt;Duke&amp;lt;/li&gt;
  &amp;lt;li&gt;Villanova&amp;lt;/li&gt;
&amp;lt;/ul&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;React asks for a manual key assigned to lists items to reduce the unnecessary computation of the indexes on the page when it changes.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My struggles with React Testing Library]]></title><description><![CDATA[<p>Open any software development book, and there is probably a section on testing and why it is essential. Testing is a great feedback tool. If you think about it, it is incredible how we can write code and then write other code to check the initial bit of code. I can't think of many professions out there who can automatically validate and verify their work like us software engineers.</p>]]></description><link>https://blog.hao.dev/my-struggles-with-react-testing-library</link><guid isPermaLink="false">https://blog.hao.dev/my-struggles-with-react-testing-library</guid><pubDate>Wed, 12 May 2021 19:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Open any software development book, and there is probably a section on testing and why it is essential. Testing is a great feedback tool. If you think about it, it is incredible how we can write code and then write other code to check the initial bit of code. I can&apos;t think of many professions out there who can automatically validate and verify their work like us software engineers.&lt;/p&gt;&lt;p&gt;Testing JavaScript functions with tools like Jest is easy. Testing React or other rendering libraries/frameworks is a different beast. React Testing Library (RTL) overtook Enzyme in popularity a few years ago and became the &quot;go-to tool&quot; for testing React apps. I&apos;ll be covering some of the issues with RTL later, but I admit it is a massive improvement over the native test kit offered by React team.&lt;/p&gt;
&lt;p&gt;In this post, we&apos;ll be mainly talking about React variant of the Testing Library. But it&apos;s worth pointing out that Testing Library doesn&apos;t just support React. It also supports other libraries/frameworks too (even Cypress).&lt;/p&gt;
&lt;h2&gt;Integration testing or unit testing?&lt;/h2&gt;
&lt;p&gt;To clarify, my use of the phrase &quot;integration test&quot; here means Cypress test that tests the UI in the browser with mocked responses. When I talk about &quot;unit test&quot;, I&apos;m referring to the sort of test you&apos;d write to test a pure JavaScript function.&lt;/p&gt;
&lt;p&gt;I struggle with RTL because I can never determine if I&apos;m writing a unit test or integration test. When I write tests with RTL, I&apos;m telling myself these are unit tests. But it always feels like I&apos;m writing integration tests. Things are rendered in a virtual DOM; we then inspect the DOM to find the elements we need. Isn&apos;t this basically Cypress?&lt;/p&gt;
&lt;p&gt;So naturally, teams start by writing RTL tests for smaller components. Then they write the same sort of test for a higher-level component, which wraps multiple nested components. Then there are page-level or complex components that make API requests or contains side effects. These tests will often render even more nested components in the virtual DOM, mixed with data fetching, side-effects that cause re-rendering.&lt;/p&gt;
&lt;p&gt;You might say: &quot;No, that&apos;s not right, we should be testing basic components and leave page-level test to E2E tools like Cypress&quot;.&lt;/p&gt;
&lt;p&gt;Well, just about every team (different companies) I joined in the past 3+ years all decided Cypress tests are too slow, CI pipeline end up taking too long, so let&apos;s migrate them to Jest and write them in RTL. I was in those teams, I disagreed with the decision, but I don&apos;t blame them. It seems like the right thing to do. Especially when Cypress tests running in parallel were taking over 40 mins to complete.&lt;/p&gt;
&lt;p&gt;So should we call the simpler component test &quot;unit tests&quot; and the more complex component &quot;integration tests&quot;? Although this distinction is vague, how could we get all the developers to agree on them? One of the team I worked in even added &quot;integration&quot; into the file name to help to distinguish. Those are the tests that no one wanted to touch since they were slow to run and hard to understand.&lt;/p&gt;
&lt;p&gt;Maybe we are doing it all wrong, and we shouldn&apos;t be writing integration tests in RTL.&lt;/p&gt;
&lt;p&gt;Actually, according to Kent C. Dodds, the creator of RTL, we really should. He challenges the traditional testing pyramid model and promotes the testing trophy in the following tweet.&lt;/p&gt;
&lt;blockquote class=&quot;twitter-tweet&quot;&gt;&lt;p lang=&quot;en&quot; dir=&quot;ltr&quot;&gt;&amp;quot;The Testing Trophy&amp;quot; 🏆&lt;br&gt;&lt;br&gt;A general guide for the **return on investment** 🤑 of the different forms of testing with regards to testing JavaScript applications.&lt;br&gt;&lt;br&gt;- End to end w/ &lt;a href=&quot;https://twitter.com/Cypress_io?ref_src=twsrc%5Etfw&quot;&gt;@Cypress_io&lt;/a&gt; ⚫️&lt;br&gt;- Integration &amp;amp; Unit w/ &lt;a href=&quot;https://twitter.com/fbjest?ref_src=twsrc%5Etfw&quot;&gt;@fbjest&lt;/a&gt; 🃏&lt;br&gt;- Static w/ &lt;a href=&quot;https://twitter.com/flowtype?ref_src=twsrc%5Etfw&quot;&gt;@flowtype&lt;/a&gt; 𝙁 and &lt;a href=&quot;https://twitter.com/geteslint?ref_src=twsrc%5Etfw&quot;&gt;@geteslint&lt;/a&gt; ⬣ &lt;a href=&quot;https://t.co/kPBC6yVxSA&quot;&gt;pic.twitter.com/kPBC6yVxSA&lt;/a&gt;&lt;/p&gt;&amp;mdash; Kent C. Dodds (@kentcdodds) &lt;a href=&quot;https://twitter.com/kentcdodds/status/960723172591992832?ref_src=twsrc%5Etfw&quot;&gt;February 6, 2018&lt;/a&gt;&lt;/blockquote&gt; &lt;script async src=&quot;https://platform.twitter.com/widgets.js&quot; charset=&quot;utf-8&quot;&gt;&lt;/script&gt;
&lt;p&gt;So we should be writing integration and unit tests in the same format, layout, and with the same tools? Let me know what you think in the comment section. I&apos;m still very undecided about this 😆&lt;/p&gt;
&lt;h2&gt;Stop mocking children&lt;/h2&gt;
&lt;p&gt;Another problem with writing tests in RTL for complex components is the amount of mocking required. RTL tests render the tested component in the virtual DOM. Therefore we need to provide all the props expected by the component itself or any nested components. Otherwise, it will error, and your test is likely to fail. Mocking is manageable with smaller or simpler components that either doesn&apos;t have much data passed to it or are relatively flat in the tree structure.&lt;/p&gt;
&lt;p&gt;I&apos;ve worked on very data-heavy apps. Complex components often required mocked responses around a few hundred (extreme ones are thousands) lines of JSON. So the standard practice is to copy the real response from the browser Network tab and paste it into a &quot;mock data file&quot;. It is probably possible to trim it down a little, but that will be more time-consuming. I&apos;ve also seen and tried to write test helper functions that generate mocked data, but these often would grow in complexity to a point it is even less maintainable than the static data copied from the browser.&lt;/p&gt;
&lt;p&gt;Maybe these are just extreme cases? Not all complex components consume that much data.&lt;/p&gt;
&lt;h2&gt;Rendering large or complex components in DOM is slow&lt;/h2&gt;
&lt;p&gt;If mocking isn&apos;t a problem you encounter every day, then perhaps slow tests annoy you enough to rethink this.&lt;/p&gt;
&lt;p&gt;Complex components tend to have side-effects, e.g. request data with &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt;, when external data comes back, update component state with &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt;, then re-render to display that data.&lt;/p&gt;
&lt;p&gt;One of these components can take up to a few hundred milliseconds to render. Now imagine a modest number of 20 test cases in a test suite; this already adds up to a few seconds. Let&apos;s face it, 20 test cases per page level component is a very conservative estimation. If you prefer Test-Driven Development (TDD) or simply want to modify a few tests, it can be painfully slow to wait for the tests to run each time you change anything. I often deal with individual test suites that take 20 - 60 seconds to run (I will cover why in the next section).&lt;/p&gt;
&lt;p&gt;I have been utilising Jest&apos;s .skip and .only to limit the number the test cases I run when writing tests. I also only run Jest on one file at a time. Otherwise, waiting around for all the test to finish would drive me mad. And then there are the async tests.&lt;/p&gt;
&lt;h2&gt;Await, waitFor, what are we actually waiting for?&lt;/h2&gt;
&lt;p&gt;RTL supports async tests. As we mentioned already, it is effectively doing what Cypress is doing, the only difference is that it does it in the virtual DOM. So when the page re-renders after a user interaction or data request, it needs to wait for the page/component to finish re-rendering.&lt;/p&gt;
&lt;p&gt;In my experience, when you are testing complex components, you may as well assume something needs waiting on. That&apos;s right, start all of the test cases assuming it is async unless proven otherwise.&lt;/p&gt;
&lt;p&gt;I&apos;ve battled with await and &lt;code class=&quot;language-text&quot;&gt;waitFor()&lt;/code&gt; (RTL&apos;s built-in API for waiting for stuff to happen) a lot recently. A few months ago, we increased the timeout to 10 times the default value because unit tests were too flaky. Yeah, those flaky integrations or E2E tests are in our unit tests now. Luckily, after increasing the timeout to 10s, tests seem to be more stable now. But it also means when a test fails, Jest will also wait for 10s before marking it as a failing test. Now we have an interesting scenario if the test takes too long to finish, then probably it is because there are failing tests.&lt;/p&gt;
&lt;h2&gt;RTL tests are hard to write&lt;/h2&gt;
&lt;p&gt;It shouldn&apos;t shock anyone that Cypress tests are laborious and slow to write because it is annoying to write the different selectors for elements on the page. Now, let&apos;s try to do that without seeing and inspecting the page at all. Because there is no way to do that in RTL, the best you can do is use RTL&apos;s &lt;code class=&quot;language-text&quot;&gt;debug()&lt;/code&gt;, which prints out content in the virtual DOM. If you want to be fancy, you can first select a sub-component that contains the element you need, and we can call it &lt;code class=&quot;language-text&quot;&gt;wrapperComponent&lt;/code&gt;. Then we can do &lt;code class=&quot;language-text&quot;&gt;debug(wrapperComponent)&lt;/code&gt;, which will only print out the selected element and its nested elements.&lt;/p&gt;
&lt;p&gt;Recently, I was so stuck with an async RTL test that keeps complaining about how it cannot find the element I&apos;m looking at, but I can see it when I print it out with &lt;code class=&quot;language-text&quot;&gt;debug()&lt;/code&gt;. I went as far as installing jest-image-snapshot to see what RTL is rendering. I think this is enough evidence to justify why writing an RTL test for complex components is challenging.&lt;/p&gt;
&lt;h2&gt;RTL tests are harder to maintain&lt;/h2&gt;
&lt;p&gt;If you think writing RTL tests are complicated, now imagine opening up an RTL test suite someone else wrote and try to fix broken tests because you changed a small thing in a nested component. Hopefully, Jest&apos;s error message is helpful enough to spot the issue. Otherwise, there is no other way other than to use RTL debug() to check what is going on.
Let&apos;s take it a step further, and say you made an app-wide change. Now several complex component test suites are failing. Well then, have fun!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Cost Driven Development]]></title><description><![CDATA[<p>Are you a fellow software engineer who had great product or business ideas then abandoned them? Have you been put off the idea due to hosting and running costs? I think we can all agree that it would be a shame if a million pounds idea got buried due to early-stage costs. At the same time, we need to do our best to navigate the money-grabbing realities of SaSS and platforms out there. Sure, a few pounds for a domain here and a few pounds to spin up a server there might not sound much on their own. However, when combined, they could quickly deplete our savings, which is not what we want. Continue reading, and I'll be sharing some of my thoughts and tips around how to cheaply or even create side projects for free.</p>]]></description><link>https://blog.hao.dev/cost-driven-development</link><guid isPermaLink="false">https://blog.hao.dev/cost-driven-development</guid><pubDate>Thu, 04 Mar 2021 19:48:00 GMT</pubDate><content:encoded>&lt;p&gt;Are you a fellow software engineer who had great product or business ideas then abandoned them? Have you been put off the idea due to hosting and running costs? I think we can all agree that it would be a shame if a million pounds idea got buried due to early-stage costs. At the same time, we need to do our best to navigate the money-grabbing realities of SaSS and platforms out there. Sure, a few pounds for a domain here and a few pounds to spin up a server there might not sound much on their own. However, when combined, they could quickly deplete our savings, which is not what we want. Continue reading, and I&apos;ll be sharing some of my thoughts and tips around how to cheaply or even create side projects for free.&lt;/p&gt;&lt;h2&gt;Developing software is cheap when it only requires our time&lt;/h2&gt;
&lt;p&gt;As developers, we are the perfect group of people to experiment and trial digital business/ideas with extremely little commitment, which essentially boils down to our time. Countless open source libraries are at our disposal to speed up prototyping and coming up with an MVP from our idea. Maybe one of these projects will become the opportunity for you to quit the day job, make a lot of money, take control, or whatever your dream may be. We should understand how lucky we are; business people with ideas who lack technical skills can only dream of our situation.&lt;/p&gt;
&lt;p&gt;Of course, building any product can be complex and time-consuming. If we want to make a quality product loved by users, it is even more challenging and demanding. However, this doesn&apos;t change the fact we don&apos;t need capital to create Proof Of Concepts (POC) to try out business or product ideas. As we all know, hiring us developers is expensive.&lt;/p&gt;
&lt;p&gt;Now, we&apos;ve established the fact being software developers give us a massive edge over everyone else. What stands between us and the idea we have is simply time. So, what are the other costs we need to consider?&lt;/p&gt;
&lt;h2&gt;Hosting an application is expensive&lt;/h2&gt;
&lt;p&gt;Since it is so cheap for us to work on side projects, I tend to work on poorly considered projects. Despite this, it is still an excellent exercise because I&apos;m learning in the process, and I can showcase these projects to people. I&apos;m a big fan of the concept &quot;succeed even when you fail&quot;, whereby mixing in some learning, you are guaranteed to walk away with a smile even when the business or product idea does work out. For example, creating a side project using Svelte may not reach the Facebook level of success. Still, by the end, the enhanced Svelte skill might be excellent if a Svelte job opportunity presents itself.&lt;/p&gt;
&lt;p&gt;These &quot;low cost&quot; personal projects often lead to a large number of apps created. Naturally, we&apos;d want to share them with the world considering we put in time and energy.  However, hosting these applications often comes with some costs. It could be as essential as domain names, Continuous Integration (CI) pipeline, etc. I would categories these costs in two ways: 1) baseline fees, these are the charges that you cannot avoid, e.g. minimum requirement to ensure the application is running, 2)  usage fees, cost that increase with usage.&lt;/p&gt;
&lt;p&gt;The first cost category is easy to predict but hard to avoid. The good news is that these costs tend to be on the cheaper side. That is as long as you are flexible, e.g. you can probably pick a domain name that&apos;s as cheap as a few pounds or more than thousands. For an unproven side project, I&apos;d personally lean on the cheaper side and get a domain that&apos;s &quot;good enough&quot;.&lt;/p&gt;
&lt;p&gt;The second category is harder to predict, but often there&apos;ll be a free-tier. Unless you are getting some decent traffic or load to the application, it is unlikely to cost too much. Just be careful because these tiers are there to draw you in, so it doesn&apos;t take much for the cost to rack up, e.g. someone might decide to crawl your site a few times or DDoS it for fun will send your bill rocketing. On the other hand, if the product is attracting legitimate users, which increases cost, this could be problematic if there isn&apos;t a business model to generate revenue.&lt;/p&gt;
&lt;h2&gt;This is where Cost-saving Driven Development comes in&lt;/h2&gt;
&lt;p&gt;What do I mean by Cost-saving Driven Development? As the name suggests, we are putting cost as the absolute centre when designing and writing software. In my opinion, projects that aren&apos;t sustainable on their own isn&apos;t worth having. Thus minimising cost shouldn&apos;t be one of the goals; it is the only goal. Is a portfolio of applications that costs hundreds of pounds a month to run worth having around, on the odd chances some mystical future employer stumbles on to it? I would argue a Github portfolio might be better in this case. Take some screenshots and videos to demo your work if the codebase by itself is too boring on its own.&lt;/p&gt;
&lt;p&gt;It is too easy to create a technical design and solution to a convoluted and expensive problem in today&apos;s environment. There are so many SaSS products out there waiting to grab hold of our wallets, failing that there are the big boys in town. Yes, I&apos;m looking at you AWS, Google Cloud, etc. Did you know that Amazon gets 70% of its profit from AWS alone (&lt;a href=&quot;https://www.bbc.co.uk/news/business-50728082&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Where the money is really made at Amazon&lt;/a&gt;)? Guess where the money came from? That&apos;s right, businesses and individuals like us.&lt;/p&gt;
&lt;p&gt;Unlike a profitable business, as individuals, we don&apos;t have lots of money to splash around. Personally, I have very little incentive to throw money at several projects that I don&apos;t know will succeed or not. I&apos;d probably be better off to invest that money in the stock market. Therefore, the cost is always the first thing I think about when I work on a side project.&lt;/p&gt;
&lt;p&gt;I have dedicated a whole section below on a list of my favourite services and platforms for creating side projects. But here are some general tips that might lead to some cost savings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Instead of buying a domain for each product, use one domain name for all of them. E.g. I could use &lt;code class=&quot;language-text&quot;&gt;*.hao.dev&lt;/code&gt; to host different apps.&lt;/li&gt;
&lt;li&gt;Consider bring in some revenue to support more popular projects. Get donation, sponsorship, advertising, or any other way you can think of and bring in some money to pay for the bills.&lt;/li&gt;
&lt;li&gt;Researching all the tools and services can be tedious and time-consuming. Reading comparison articles can be a fast and effective way to get a rough idea of the best options out there.&lt;/li&gt;
&lt;li&gt;Search around for promotions, free credits or sign up discount codes to maximise your allowances or benefits. Services and platforms often use these techniques to bring in new customers.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Another take on Cost-saving Driven Development from a professional angle&lt;/h2&gt;
&lt;p&gt;As software engineers, costs aren&apos;t something we consider in our jobs until the middle management level. Therefore, it is understandable for developers not to think about cost and cost-saving. I find there are always many options to achieve the same goal using various techniques. For example, I implemented search functionality on this blog utilising a SaSS service called &lt;a href=&quot;https://www.algolia.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;algolia&lt;/a&gt;, which focuses on searching instead of building a search API myself. I could have implemented a serverless API that performs searches and returns relevant results. Taking a step further, there are even ways to generate a JSON file during the build stage, which then can be used as a &quot;fake&quot; search API (see &lt;a href=&quot;https://github.com/sw-yx/netlify-plugin-search-index&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://github.com/sw-yx/netlify-plugin-search-index&lt;/a&gt;). All of these approaches have their benefits and drawbacks. We should always consider them all, then made an informed decision.&lt;/p&gt;
&lt;p&gt;I once heard a client saying that their company AWS bill was £2 million a month. Why? Unused servers and other cloud resources are significant contributors. Allocating too much resource is a common problem as well. In my opinion, costs should be at the centre of cloud infrastructure. Each pound spent should be intentional and be reviewed after a while to see if it still makes sense. The more mature engineering teams have reporting setup to see where the costs are coming from, but too often, cost is a byproduct and not a constraint.&lt;/p&gt;
&lt;p&gt;One of the reasons why companies and business have a massive &quot;tech&quot; bill is because people often blindly following approaches defined as the &quot;common approach&quot; or &quot;best practices&quot; by companies or industry experts. In these situations, teams would disassociate cost from the technical implementation details altogether. If option A saves the company thousands of pounds and option B is slightly more brutal to work with but free, shouldn&apos;t we consider both options before jumping on the first one and calling it a day?&lt;/p&gt;
&lt;p&gt;As engineers, we are constantly told it is better to over-engineer and cater for possible unexpected developments later. Based on my personal experience, I&apos;m not sure this is true. Over-engineering is expensive in terms of development time. Additional unknown factors and the extra architecture setup are reflected in the cost to accommodate potential future changes.&lt;/p&gt;
&lt;p&gt;Part of me thinks it is unfair that we make fun of business people who always talk about costs, want things done yesterday and has no regard for software quality. Aren&apos;t we also a little hypocritical to value code quality over time and never consider the cost side?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4KS3xDXerT4BkIaAvcOxwL/5e906f69a2727865a7653ea83596e6a2/IMG_193906775646-1.jpeg&quot; alt=&quot;cost time quality diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;I think many people jump on the latest trend without really considering the actual benefits for the company or business, whilst others refuse to move on from the tech stack they are familiar with. It isn&apos;t good to be in either of the two extremes. Instead, we should all aim to be somewhere in the middle.&lt;/p&gt;
&lt;p&gt;I think putting cost at the centre will allow people to have more concrete debates about technical implementations instead of opinion battles. In dev chats, people often argue over different approaches and require a higher power to intervene when they reach a stalemate. For example, someone might say, &quot;We should go with SSR because it performs better on low-end devices&quot;. Someone else will argue, &quot;CSR can be optimised to work just as well&quot;. By considering cost, the conversation might look like this, &quot;SSR will cost £x and add additional complexity to our approach, whereas CSR cost 1/10 that much but may need longer development time to optimise the performance on low-end devices&quot;. Placing a limitation such as cost will facilitate discussion and reduce the scope of possibilities makes sure people are no longer arguing about ideologies and talking about practical solutions. &lt;/p&gt;
&lt;p&gt;Introducing limitation doesn&apos;t remove our ability to innovate. Rather, it pushes us to find ways around those constraints. There&apos;s a quote I love from the book &lt;a href=&quot;https://www.goodreads.com/book/show/34507927-how-to-take-smart-notes&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;How to Take Smart Notes&lt;/a&gt;: &quot;the limitation of canvas does not reduce the artistic expression of artists&quot;.&lt;/p&gt;
&lt;h2&gt;Here are the best cheap or free services I&apos;m using&lt;/h2&gt;
&lt;p&gt;The services I use are either free or has a basic free-tier. I have checked and made sure the allowances given as part of the free-tier is more than enough for side projects. If you get close to them, then maybe it is worth considering monetising these projects to cover the extra costs?&lt;/p&gt;
&lt;p&gt;My workflow is straightforward, and I usually start working on a project idea in &lt;a href=&quot;https://codesandbox.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;CodeSandbox&lt;/a&gt;. It is as easy as opening up my browser on any machine, and then I could start coding away. The path of least friction is critical for me at this stage of the process, where the most important thing is to visualise the idea. It is possible to create web apps like this, and CodeSandbox will host it for free. My advice is to move into local development as more files and computation will cause a slowdown.&lt;/p&gt;
&lt;p&gt;Once the project has a few pages, this is when I&apos;d migrate the code into a &lt;a href=&quot;https://github.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github&lt;/a&gt; repository. Since they made private repository free a couple of years ago, there is no reason to go with anything else. &lt;a href=&quot;https://about.gitlab.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Gitlab&lt;/a&gt;, &lt;a href=&quot;https://bitbucket.org/product&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Bitbucket&lt;/a&gt;, and other alternatives are all fine too but I always end up coming back to Github.&lt;/p&gt;
&lt;p&gt;My go-to choice for CI and CD pipeline is &lt;a href=&quot;https://www.netlify.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Netlify&lt;/a&gt;. I am getting close to the CI build allowance. When I go over it, I might decide to pay for this as it has made my life so much easier. The only other real competitor to Netlify is &lt;a href=&quot;https://vercel.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Vercel&lt;/a&gt;. Of course, Github actions is also very competitive, but Netlify and Vercel are more straightforward, more powerful, and there&apos;s something nice about having a dedicated CI pipeline.&lt;/p&gt;
&lt;p&gt;Netlify and Vercel also come with other added benefits such as On-Demand Environments (ODE), essentially creating a preview for each pull request you make, allowing you to test out the app before merging the code to master. They both host the static files (e.g. HTML, JavaScript, and more). They have built-in CDNs for custom domains, redirects, etc.&lt;/p&gt;
&lt;p&gt;Netlify and Vercel support analytics, but both platforms have it under paid-tiers. I decided to stick with Google Analytics for now because it is... you guessed it, free! There are also a number of other alternatives with better privacy promises, e.g. &lt;a href=&quot;https://matomo.org&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Matomo&lt;/a&gt;. On the other hand, &lt;a href=&quot;https://segment.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Segment&lt;/a&gt; seems to be a popular choice for businesses.&lt;/p&gt;
&lt;p&gt;I use a headless CMS for storing my data if I&apos;m creating an application that is content-heavy. My top choice right now is &lt;a href=&quot;https://prismic.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prismic&lt;/a&gt; with &lt;a href=&quot;https://www.contentful.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Contentful&lt;/a&gt; at a close second. If the application needs to store user-generated data, my go-to database service is &lt;a href=&quot;https://fauna.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;FaunaDB&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I have covered some of these services in more depths here: &lt;a href=&quot;https://blog.hao.dev/awesome-tools-to-use-in-personal-projects&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Awesome tools to use in personal projects&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;As a software engineer, I love to experiment and work on personal projects, with the dream of one day creating a product that goes viral. Needless to say, this is both a luck-based exercise as it is through hard work. It will take many, many attempts at different ideas to be successful. Any failed experiments can be costly if we are not careful, not just our time and energy but may even leave our pockets empty. So it makes sense for us to make minimising cost our number one priority. Only by taking the financial burden off our shoulders can we enjoy the process of working on personal projects. Knowing that if the project isn&apos;t successful, we have nothing to lose, only valuable experience and learning to gain, is comforting and motivating.&lt;/p&gt;
&lt;p&gt;Finally, based on my personal experience with cost-saving, there&apos;s a point where it&apos;ll give diminishing returns. Don&apos;t end up spending months researching the cheapest way to do something. Just be pragmatic and practical with this whole concept.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Hello 2021, I'm late to the party]]></title><description><![CDATA[<p>Hello friends, it's been a while since I published anything on this blog, it's been so long since I last posted anything that it is a little embarrassing. I have gone off the grid because I was just a little occupied from a few personal projects in the past few months. With that said, the Xmas break was a perfect time to recharge my batteries.</p>]]></description><link>https://blog.hao.dev/hello-2021-i-m-late-to-the-party</link><guid isPermaLink="false">https://blog.hao.dev/hello-2021-i-m-late-to-the-party</guid><pubDate>Sun, 07 Feb 2021 22:52:00 GMT</pubDate><content:encoded>&lt;p&gt;Hello friends, it&apos;s been a while since I published anything on this blog, it&apos;s been so long since I last posted anything that it is a little embarrassing. I have gone off the grid because I was just a little occupied from a few personal projects in the past few months. With that said, the Xmas break was a perfect time to recharge my batteries.&lt;/p&gt;&lt;h2&gt;Learning about note-taking and changes in workflow&lt;/h2&gt;
&lt;p&gt;One of my projects is probably going to sound bonkers - I was too busy tidying up my notes. Or using the big words, I was learning, adapting and improving my note-taking method. Why? To be more effective at managing what I take in and how I process them and convert them to content so others can consume, like this post.&lt;/p&gt;
&lt;p&gt;I have many notes saved on the topic of &quot;how to take notes effectively&quot;, but I intend to leave it for another day. This post was meant to be a quick welcome back after all.&lt;/p&gt;
&lt;h2&gt;Changes to this blog&lt;/h2&gt;
&lt;p&gt;If this isn&apos;t the first time you are here, another change you probably have noticed is the domain name. Yep, I&apos;ve recently migrated my blog from &lt;a href=&quot;https://haodong.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;haodong.io&lt;/a&gt; to this one &lt;a href=&quot;https://blog.hao.dev&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;blog.hao.dev&lt;/a&gt; . For a long time, I wanted to do this, ever since I purchased the current domain back in Jun 2020. At one point, I just decided enough was enough, and that I need to prioritise this over other tasks. As soon as I finished the note-taking migration project, I jumped on this.&lt;/p&gt;
&lt;p&gt;There is a reason why the blog is on the &lt;code class=&quot;language-text&quot;&gt;blog&lt;/code&gt; subdomain, I may write about it at some point, but honestly, I am more motivated to turn my plan into reality. In short, I wanted to add a few sections to my site, and it felt weird, stitching them to this blog. The plan from now on is to split up this site into multiple applications by sub-domain. Then it would enable me to have an isolated place to publish blog posts, a place to deploy some side projects, and a proper welcome area which can link off to the different areas rather than presenting first-time visitors a list of blogs.&lt;/p&gt;
&lt;p&gt;Finally, I have always been interested in writing about other topics that always fascinated me. I debated if I should start new blogs for each topic I want to publish. I had this feeling that mixing the different contents might not be the right thing to do, in case people who are interested in coding may not find the other topics very interesting. But in the end, I decided to keep things simple and publish it all on this blog. The other reason is that I have such a tiny audience it is not like anyone would be upset anyway 😂&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;Lastly, I would like to say welcome to 2021, even though it&apos;s already February. 2020 wasn&apos;t a brilliant year for many reasons, here&apos;s hoping this would be a better year for all of us! As well as looking forward to getting out properly, I am looking excited to see if the ways of working changes I&apos;ve adopted will get me to publish more content or not!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to use different git emails for personal and work repositories on the same machine]]></title><description><![CDATA[<p>I recently started working on some side hustle projects. To keep things organised, I created a new git account with a different email. When I tried to push to the git repository within the new git account, I got the following error.</p>]]></description><link>https://blog.hao.dev/how-to-use-different-git-emails-for-personal-and-work-repositories-on-the-same-machine</link><guid isPermaLink="false">https://blog.hao.dev/how-to-use-different-git-emails-for-personal-and-work-repositories-on-the-same-machine</guid><pubDate>Tue, 06 Oct 2020 18:07:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently started working on some side hustle projects. To keep things organised, I created a new git account with a different email. When I tried to push to the git repository within the new git account, I got the following error.&lt;/p&gt;&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;Enumerating objects: &lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;, done.
Counting objects: &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;% &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;26&lt;/span&gt;/26&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, done.
Delta compression using up to &lt;span class=&quot;token number&quot;&gt;12&lt;/span&gt; threads
Compressing objects: &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;% &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;/16&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, done.
Writing objects: &lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;% &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt;/21&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;token number&quot;&gt;69.90&lt;/span&gt; KiB &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4.37&lt;/span&gt; MiB/s, done.
Total &lt;span class=&quot;token number&quot;&gt;21&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;delta &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;, reused &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;delta &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
remote: GitLab: Author &lt;span class=&quot;token string&quot;&gt;&apos;personal@mail.com&apos;&lt;/span&gt; is not a member of team
To gitlab.work.co.uk:project/repo.git
 &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;remote rejected&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; feature/a -&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; feature/a &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;pre-receive hook declined&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
error: failed to push some refs to &lt;span class=&quot;token string&quot;&gt;&apos;git@gitlab.work.co.uk:project/repo.git&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(I removed some sensitive data.)&lt;/p&gt;
&lt;p&gt;I didn&apos;t want to change my default git email, because I still want to use it for other git repositories. After some research, I&apos;ve found two ways to solve my problem.&lt;/p&gt;
&lt;h2&gt;Method 1: use different git email based on repository path&lt;/h2&gt;
&lt;p&gt;This method involves adding some conditions to global git config. Then within each git repo, it will automatically figure out which email to use based on the path.&lt;/p&gt;
&lt;p&gt;By default, the global git config looked something like this for me.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ~/.gitconfig&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; personal@email.com
    name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Hao Dong&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;1. Rename &quot;.gitconfig&quot; to &quot;.gitconfig-personal&quot;&lt;/h3&gt;
&lt;p&gt;Rename the global config to &lt;code class=&quot;language-text&quot;&gt;~/.gitconfig-personal&lt;/code&gt; using the following command. It will now be our new config for any personal projects.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;mv&lt;/span&gt; .gitconfig .gitconfig-personal&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Don&apos;t worry, we will add the global config back later.&lt;/p&gt;
&lt;h3&gt;2. Create &quot;.gitconfig-work&quot;&lt;/h3&gt;
&lt;p&gt;Create the new config file for work.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; ~/.gitconfig-work&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add the following into the config file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ~/.gitconfig-work&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    email &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; work@email.com
    name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Hao Dong&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;3. Create a new &quot;.gitconfig&quot;&lt;/h3&gt;
&lt;p&gt;Add a new global &lt;code class=&quot;language-text&quot;&gt;.gitconfig&lt;/code&gt; file, this will point to the other two custom config files.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;touch&lt;/span&gt; ~/.gitconfig&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then add the following into the global config file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;# ~/.gitconfig&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;includeIf &lt;span class=&quot;token string&quot;&gt;&quot;gitdir:~/work/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; .gitconfig-work
&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;includeIf &lt;span class=&quot;token string&quot;&gt;&quot;gitdir:~/personal/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; .gitconfig-personal&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://git-scm.com/docs/git-config#_includes&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://git-scm.com/docs/git-config#_includes&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Method 2: setting git email per repository&lt;/h2&gt;
&lt;p&gt;Personally, I would suggest going with Method 1, no matter your situation. But if you are confident it is a one-off case, then this method might be appropriate.&lt;/p&gt;
&lt;p&gt;Navigate to the work repository, then at the root folder run the following command to change the email.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; config --local user.email name@work.com&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: this command only affects the current repository. Any other repositories will still use the default email specified in &lt;code class=&quot;language-text&quot;&gt;~/.gitconfig&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Question: I did all that, but the commit still says the wrong email&lt;/h2&gt;
&lt;p&gt;If you already committed the code change like I have, then you will have to go through each commit made prior to the email change and then update the email in the commit using the following command.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token function&quot;&gt;git&lt;/span&gt; commit --amend --author&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Full Name &amp;lt;email@address.com&gt;&quot;&lt;/span&gt; --no-edit &lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[How to share a Roam page with others?]]></title><description><![CDATA[<p>Roam is still in active development right now, which means some common features in other note-taking app is not quite as easy to use here. One of these features is the ability to share a single page of note with friends or family. In this blog post, I'll show you how to do this properly.</p>]]></description><link>https://blog.hao.dev/how-to-share-a-roam-page-with-others</link><guid isPermaLink="false">https://blog.hao.dev/how-to-share-a-roam-page-with-others</guid><pubDate>Wed, 30 Sep 2020 21:22:00 GMT</pubDate><content:encoded>&lt;p&gt;Roam is still in active development right now, which means some common features in other note-taking app is not quite as easy to use here. One of these features is the ability to share a single page of note with friends or family. In this blog post, I&apos;ll show you how to do this properly.&lt;/p&gt;&lt;p&gt;Firstly, I just to make it clear that there are two ways to share notes in Roam. The first way is easier to find in the menu, but it may expose your entire notes on Roam to the public. The second way is what we want, which only makes one specific page publicly accessible.&lt;/p&gt;
&lt;p&gt;Go to the page you wish to share, then click on the three-dot menu. You should see similar options like the ones shown in the image below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1t1uxPWq3Cs4QVwjPChKZM/d3cc84befbc4f760b113ee3cf2e8407e/Screenshot_2020-09-30_at_21.48.40.png&quot; alt=&quot;Roam initial three-dot menu&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you click on &lt;strong&gt;Share&lt;/strong&gt;, then you should see the following pop up appearing. There is a warning message saying this is still an experimental feature, so use this with caution, there could be bugs where when you share a single page Roam may accidentally make other pages linked publicly accessible as well.&lt;/p&gt;
&lt;p&gt;If you are happy to continue, then toggle &quot;Page Permissions&quot; on. The toggle button should now be active (blue).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7sir6MYlpAXazGT1a7YJZo/55373ed088250b65311a9cc8be3a8a93/Screenshot_2020-09-30_at_21.47.39.png&quot; alt=&quot;Roam database permission with page permission turned on&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now close the pop-up, and click on the three-dot menu again. This time you should see &quot;Share Page&quot; Option similar to the one below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6W2u9NpNE6Ei2fvBHSu73M/758742a0a4682cbab160f61367871fc5/Screenshot_2020-09-30_at_22.08.55.png&quot; alt=&quot;Roam three-dot menu with Share page option&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click on it, and a different pop-up will appear. By default, the page permission is set to &lt;strong&gt;Private&lt;/strong&gt;,  change it to &lt;strong&gt;Public Readonly&lt;/strong&gt; then close the pop-up window.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7oc5T2ALR8IQYVuWKY7ghm/716c2488815ee4e42655c1467d3fc457/Screenshot_2020-09-30_at_22.02.19.png&quot; alt=&quot;Roam page permission set to public readonly&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then click on the three-dot menu again, this time select &lt;strong&gt;Share link to this page&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4AbjSaEKy9yjo6nna8wfwN/892f84b40e1e979a68e9ab25718f7793/Screenshot_2020-09-30_at_22.09.01.png&quot; alt=&quot;Roam share link to this page&quot;&gt;&lt;/p&gt;
&lt;p&gt;Clicking on the menu option should automatically copy the page link to your clipboard. At this stage, you should see the following alert notification if everything is working as expected.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/BdIpMYjwlCrlpuWuyntTd/b36120aff849f7dad3fe9b9da4757698/Screenshot_2020-09-30_at_22.09.52.png&quot; alt=&quot;Roam page link copied alert&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now the link is ready to be shared with the right people!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Boost your productivity with Google Calendar shortcuts]]></title><description><![CDATA[<p>I use Google Calendar, both personally and professionally. I have a shared calendar with a friend to keep track of different events and activities. I have always found it hard to navigate Google Calendar in the browser, I love the look and feel of the web app, though I find myself always looking for the right buttons, etc. I knew about the brilliant shortcuts that made Gmail fun to use and decided to see if Google Calendar offered something similar. Luckily, they do, and they are straightforward to remember.</p>]]></description><link>https://blog.hao.dev/boost-your-productivity-with-google-calendar-shortcuts</link><guid isPermaLink="false">https://blog.hao.dev/boost-your-productivity-with-google-calendar-shortcuts</guid><pubDate>Tue, 22 Sep 2020 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I use Google Calendar, both personally and professionally. I have a shared calendar with a friend to keep track of different events and activities. I have always found it hard to navigate Google Calendar in the browser, I love the look and feel of the web app, though I find myself always looking for the right buttons, etc. I knew about the brilliant shortcuts that made Gmail fun to use and decided to see if Google Calendar offered something similar. Luckily, they do, and they are straightforward to remember.&lt;/p&gt;&lt;h2&gt;Changing Views&lt;/h2&gt;
&lt;p&gt;I am always changing between Day, Week or Month views. I use Week view to plan and check upcoming events that I should be aware of for the week. I use Day view when I want to focus. Month view is convenient for a lot of other tasks, despite I only use this occasionally.&lt;/p&gt;
&lt;p&gt;During the day I&apos;m continually changing between Day and Week views, sometimes even Month view. Which means each time I open up the calendar, it is often not showing me what I want to see. I end up clicking around a whole bunch to get what I&apos;m interested in to display.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/01GXH12icyBZZ6TWsiZIdu/8cbb5c75f63fe92953b0b8f83f094897/Screenshot_2020-09-22_at_19.32.10.png&quot; alt=&quot;Google Calendar views&quot;&gt;&lt;/p&gt;
&lt;p&gt;As it turns out, there are shortcuts to switch straight to these views:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;D&lt;/code&gt; Day View&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;2&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;W&lt;/code&gt; for Week View&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;3&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;M&lt;/code&gt; for Month View&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Quick Access To Calendar Search&lt;/h2&gt;
&lt;p&gt;When searching for events that you might not know the exact date range, it is useful to use the search feature built into Google Calendar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6gNBtHWfxD1v7tje1AUpms/6d698fceafd44b8485c233725c0fbe32/Screenshot_2020-09-22_at_19.34.28.png&quot; alt=&quot;Google Calendar Navigation Bar&quot;&gt;&lt;/p&gt;
&lt;p&gt;Most people probably don&apos;t even notice the small search icon in the top menu bar. It is also quite fiddly to click because of how little it is.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;/&lt;/code&gt; will put your cursor directly in the search box, therefore making it easier to search for events quickly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5MakFE6r7JErT3ikAL9mSv/ace88d86ef51f693e8b6a770de497b79/Screenshot_2020-09-22_at_19.29.17.png&quot; alt=&quot;Google Calendar Search&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Navigate Date Ranges&lt;/h2&gt;
&lt;p&gt;I mainly plan in Week view because it is not possible to see everything clearly in Month view. However, I often need to go back and forth between the current week and the next couple of weeks. The chevron buttons (&quot;&amp;#x3C; &quot;and&quot;&gt;&quot; respectfully) at the top menu bar are for navigating back and forth. They are a pain to use because of how tiny they are, and the round shape makes the clickable area even smaller.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1meBiZvNJXz5bbqa5cj8VL/c146650ea3cd5e0a8e43df6435866e1f/Screenshot_2020-09-22_at_19.40.44.png&quot; alt=&quot;Google Calendar Navigation&quot;&gt;&lt;/p&gt;
&lt;p&gt;Luckily, we have a few easy shortcuts to get the job done.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;N&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;J&lt;/code&gt; moves the calendar view to the next date range. e.g. go forward one week in Week view&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;P&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;K&lt;/code&gt; moves the calendar view to the previous date range. e.g. go back one week in Week view&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;T&lt;/code&gt; moves to the current day. Very useful for getting back to the current week.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;G&lt;/code&gt; will open a popup allowing you to specify a date and then move to the date, which is useful for hopping around dates that are far from the current date range.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Additional shortcuts&lt;/h2&gt;
&lt;p&gt;Personally, these doesn&apos;t fit well into my workflow. However, each person works differently. So some may also find the following shortcuts useful to know.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;4&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;X&lt;/code&gt; for Custom View&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;5&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;A&lt;/code&gt; for Agenda View&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;6&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Y&lt;/code&gt; for Year View&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;R&lt;/code&gt; to refresh when using Google Calendar on multiple devices/tabs&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;C&lt;/code&gt; to create a new event&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Backspace&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;Delete&lt;/code&gt; to delete an event&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Esc&lt;/code&gt; will return to calendar grid from an event details page&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For more information, you can check out &lt;a href=&quot;https://support.google.com/calendar/answer/37034?hl=en&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google&apos;s official doc&lt;/a&gt; on this.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Time to go back to the fundamentals of software development?]]></title><description><![CDATA[<p>With all the new toys coming out each day, libraries, frameworks and even architectures. Have we forgotten the fundamentals of software development? Every developer wants to write good code, but how often do we do what we say? Maybe, every so often we all need to take a step back and rethink our approaches to software development. If nothing else, we should find challenges and excitement in what we do, if only to make sure the passion that drove us to this career path doesn't die out.</p>]]></description><link>https://blog.hao.dev/time-to-go-back-to-the-fundamentals-of-software-development</link><guid isPermaLink="false">https://blog.hao.dev/time-to-go-back-to-the-fundamentals-of-software-development</guid><pubDate>Thu, 10 Sep 2020 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;With all the new toys coming out each day, libraries, frameworks and even architectures. Have we forgotten the fundamentals of software development? Every developer wants to write good code, but how often do we do what we say? Maybe, every so often we all need to take a step back and rethink our approaches to software development. If nothing else, we should find challenges and excitement in what we do, if only to make sure the passion that drove us to this career path doesn&apos;t die out.&lt;/p&gt;&lt;p&gt;When I was taking my regular walk in the park, I couldn&apos;t help but notice how people perform &quot;heel strike&quot; when they jog. It is when the heel of the foot lands first when running. It isn&apos;t the most effective way to run, since each leg carries more than twice our body weight when it hits the ground, and when the heel lands force travels up the led and ends near our knees and hips. Long term jogger may find themselves with injuries in these areas. The best form of running on flat pavement, arguably, is a midfoot strike. Where the middle of the foot land first then followed by the heel. It gives the leg muscles time to absorb the force like a spring before they travel up the body.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7CCk6Wpbemy0DNvR8Y0U93/187bdb75e86cdd12db9aa624b7854319/image.png&quot; alt=&quot;Running techniques&quot;&gt;&lt;/p&gt;
&lt;p&gt;Considering our ancestors depended on running to survival in the majority of the past 200,000 years. There fore running is and should be instinctive to us, and yet we don&apos;t all seem to do it correctly for one reason or another. It just shows even the most basic and essential things in life there&apos;s a chance we might not be aware we are not doing it well.&lt;/p&gt;
&lt;p&gt;Which made me think, what else could I be doing wrong without realising?&lt;/p&gt;
&lt;p&gt;I&apos;ve been working in the software development field for over six years. It&apos;s not a crazy long time compared to people who&apos;s been doing this for 30 years. But it is still long enough for me to develop many ineffective habits or misconceptions during this period without realising. Perhaps even those I practice daily at work?&lt;/p&gt;
&lt;p&gt;Given how fast everything changes in the digital world, it is a constant battle to stay relevant, not to mention finding the time to perfect or enhance our existing skills. It might even logically make sense to tune down the perfectionist voice in our heads and focus only on getting what&apos;s right in front of us, our work, done. There is no time to be steady, methodical and think. But aren&apos;t these traits of a good software engineer?&lt;/p&gt;
&lt;h2&gt;Learn and priorities language specification changes&lt;/h2&gt;
&lt;p&gt;People are so busy looking at new shiny toys which flood our social feeds each day. Could it be possible that we forgot the more fundamental part of our skills - programming languages?&lt;/p&gt;
&lt;p&gt;All the frameworks and libraries are cool, the tools and their plugins are fantastic, and the new theme for the terminal is pretty sick! However, as long as you develop software, programming languages are at the core of what we do. Just like everything else, languages change and evolve over time. I wonder how many people actually follow the new language spec changes and study it properly? It&apos;s shameful for me to say that I&apos;m one of these people right now.&lt;/p&gt;
&lt;p&gt;There could be many reasons for this, and I think one of the major contributors is the fact most of us don&apos;t work in an environment that promotes the importance of language specification changes enough. Instead, skillset and knowledge about frameworks and libraries are more heavily promoted.&lt;/p&gt;
&lt;p&gt;Perhaps we should spend more time studying the language specification and understand the limits of the language we use every day. Often, learning the new features and design shift highlights the limitations of the older approach. It helps us to understand the why behind individual language quirks, but more importantly, we will know when &quot;best practices&quot; posted all over the internet stops being true.&lt;/p&gt;
&lt;p&gt;I once heard a senior JavaScript developer telling junior devs to never use &lt;code class=&quot;language-text&quot;&gt;forEach()&lt;/code&gt; in JavaScript due to its &quot;inefficiency&quot;. The person isn&apos;t wrong, and this &lt;a href=&quot;https://github.com/dg92/Performance-Analysis-JS&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;performance analysis&lt;/a&gt; proves it. The crucial piece of information left out is that we often never loop over arrays with a length of 50 or more. The difference is minuscule with this kind of data set, I mean we are talking about a fraction of a millisecond!&lt;/p&gt;
&lt;h2&gt;Write our own libraries&lt;/h2&gt;
&lt;p&gt;Open-source libraries are amazing. They solve the most challenging problems and present them as a nice solution, allowing us to focus on other aspects of our job. JavaScript&apos;s package manager reach its one-millionth goal back in Jun 2019. That is a lot of potentials to leverage when it comes to creating applications.&lt;/p&gt;
&lt;p&gt;Positives aside, I reckon it is also dangerous to always reach out to these 3rd party libraries whenever there&apos;s a problem. Maybe sometimes it is worth implementing a simple and even substandard piece of code instead of pulling down a library from NPM. In many ways, using software other people wrote is a dark path to go down, the more you rely on them, the less you&apos;ll learn, your mind gets lazy, and the cycle continues.&lt;/p&gt;
&lt;p&gt;I worked in a team a few years back that decided to write a custom rendering library just because it was an interesting experiment. We knew we&apos;d learn something along the way, and that we&apos;ll be able to get the job done no matter what. The result was fantastic; every member of the team had a deep understanding of the core rendering logic, fixing bugs and making optimisations couldn&apos;t have been easier. Needless to say, I feel like I learned more in that one project than ten projects using an off-the-shelf library.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What's new with TypeScript 4.0?]]></title><description><![CDATA[<p>TypeScript 4.0 just came out, and people probably want to be upgrading to it soon. Here's a quick summary of what's new based on official release notes. This is only meant to be a quick summary, so I have left out edge cases and detailed examples. If you have time, I'd recommended giving the official release note a proper read. But for those who want to get a high-level gist of what changed, this might be useful.</p>]]></description><link>https://blog.hao.dev/what-s-new-with-typescript-4-0</link><guid isPermaLink="false">https://blog.hao.dev/what-s-new-with-typescript-4-0</guid><pubDate>Fri, 21 Aug 2020 07:52:00 GMT</pubDate><content:encoded>&lt;p&gt;TypeScript 4.0 just came out, and people probably want to be upgrading to it soon. Here&apos;s a quick summary of what&apos;s new based on official release notes. This is only meant to be a quick summary, so I have left out edge cases and detailed examples. If you have time, I&apos;d recommended giving the official release note a proper read. But for those who want to get a high-level gist of what changed, this might be useful.&lt;/p&gt;&lt;p&gt;Of course, all credits goes to TypeScript team.&lt;/p&gt;
&lt;h2&gt;Variadic Tuple Types&lt;/h2&gt;
&lt;p&gt;TypeScript 4.0 introduced two fundamental changes to improve typing:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Spreads in tuple type syntax can now be generic.&lt;/li&gt;
&lt;li&gt;Rest elements can occur anywhere in a tuple, not just at the end.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It is now possible to assign better types to tuples, which validates lengths of the input and the order of the elements.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;concat&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; arr2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;

&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;jsx
type Arr &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; readonly any&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; concat&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;U&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Arr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arr1&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;arr2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;arr2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#variadic-tuple-types&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Labeled Tuple Elements&lt;/h2&gt;
&lt;p&gt;In order to improve readability, it is now possible to provide labels to tuples types.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// ealier versions&lt;/span&gt;
type Range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// TypeScript 4.0&lt;/span&gt;
type Range &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;start&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;end&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This could be useful when using tuple in the following situation. In earlier versions, there are no parameter names which can make the code harder to understand.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;args&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Range&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#labeled-tuple-elements&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Class Property Inference from Constructors&lt;/h2&gt;
&lt;p&gt;TypeScript 4.0 now uses control flow analysis to determine the types of properties in classes when &lt;code class=&quot;language-text&quot;&gt;noImplicitAny&lt;/code&gt; is enabled.&lt;/p&gt;
&lt;p&gt;In the following example, TypeScript can work out the type of &lt;strong&gt;area&lt;/strong&gt; based on the assigned type of &lt;strong&gt;sideLength&lt;/strong&gt; and the fact &lt;strong&gt;sideLength&lt;/strong&gt; was used to calculate &lt;strong&gt;area&lt;/strong&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Square&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Previously: implicit any!&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Now: inferred to `number`!&lt;/span&gt;
    area&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    sideLength&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;sideLength&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;sideLength &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sideLength&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;area &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; sideLength &lt;span class=&quot;token operator&quot;&gt;**&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;%5Bhttps://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#class-property-inference%5D(https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#class-property-inference)&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Short-Circuiting Assignment Operators&lt;/h2&gt;
&lt;p&gt;Compound assignment operators apply an operator to two arguments and then assign the result to the left side. This is very commonly used in JavaScript and other languages.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Addition&lt;/span&gt;
a &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a = a + b&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Multiplication&lt;/span&gt;
a &lt;span class=&quot;token operator&quot;&gt;*=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a = a * b&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Exponentiation&lt;/span&gt;
a &lt;span class=&quot;token operator&quot;&gt;**=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a = a ** b&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Left Bit Shift&lt;/span&gt;
a &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&amp;lt;=&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// a = a &amp;lt;&amp;lt; b&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;TypeScript 4.0 has introduced three new assignment operators:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;amp;&amp;amp;=&lt;/code&gt; that substitutes &lt;code class=&quot;language-text&quot;&gt;a = a &amp;amp;&amp;amp; b&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;||=&lt;/code&gt; that substitutes &lt;code class=&quot;language-text&quot;&gt;a = a || b;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;??=&lt;/code&gt; that substitutes &lt;code class=&quot;language-text&quot;&gt;a = a ?? b;&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Suppose you haven&apos;t seen &lt;code class=&quot;language-text&quot;&gt;??&lt;/code&gt; before (like me). It is called a nullish coalescing operator.  It is a logical operator that returns its right-hand side operand when its left-hand side operand is null or undefined and otherwise returns its left-hand side operand. Here are some examples from MDN.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; foo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;default string&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;foo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// expected output: &quot;default string&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; baz &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;baz&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// expected output: 0&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#short-circuiting-assignment-operators&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;unknown on catch Clause Bindings&lt;/h2&gt;
&lt;p&gt;From the start TypeScript uses &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; type for &lt;strong&gt;catch&lt;/strong&gt; variables. Although nothing has been done to the default &lt;strong&gt;catch&lt;/strong&gt; variables. This new version of TypeScript has allowed assignments of &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; type instead of &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; since &lt;code class=&quot;language-text&quot;&gt;unknown&lt;/code&gt; is safer.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;try&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;catch&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// x has type &apos;any&apos; - have fun!&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;message&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toUpperCase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    x&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yadda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;yadda&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;yadda&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Probably don&apos;t need to worry too much about this for now. Just know that there might be more changes coming later which will build on top of this.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#unknown-on-catch&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Custom JSX Factories&lt;/h2&gt;
&lt;p&gt;In JSX Fragments allow a component to return several child elements without specifying a wrapper parent element.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;React.Fragment&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;icon&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
        &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;submit&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Click me&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;React.Fragment&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
);&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In Typescript 4.0, it is now possible to customise the fragment factory through the new &lt;code class=&quot;language-text&quot;&gt;jsxFragmentFactory&lt;/code&gt; option in &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt; to apply this change globally.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;jsx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;react&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;jsxFactory&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;h&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;jsxFragmentFactory&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Fragment&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Alternatively, it is possible to only apply to single files using the new &lt;code class=&quot;language-text&quot;&gt;/** @jsxFrag */&lt;/code&gt; pragma comment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/** @jsx h */&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/** @jsxFrag Fragment */&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; h&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Fragment &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;preact&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; stuff &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Hello&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#custom-jsx-factories&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Speed Improvements in build&lt;/h2&gt;
&lt;p&gt;TypeScript now cache previous compilation in &lt;code class=&quot;language-text&quot;&gt;.tsbuildinfo&lt;/code&gt; file, to speed up compilation with &lt;code class=&quot;language-text&quot;&gt;--incremental&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;--noEmitOnError&lt;/code&gt; option after a previous compile error.&lt;/p&gt;
&lt;p&gt;It is now also possible to use &lt;code class=&quot;language-text&quot;&gt;--incremental&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;--noEmit&lt;/code&gt; options and still take advantage of the faster build.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#build-and-noemitonerror&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h2&gt;Editor changes&lt;/h2&gt;
&lt;p&gt;I won&apos;t go through them too much here, as they are not likely to affect most people. Here are one sentence explanations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;TypeScript&apos;s editing support now recognises when a declaration has been marked with a &lt;code class=&quot;language-text&quot;&gt;/** @deprecated *&lt;/code&gt; JSDoc comment.&lt;/li&gt;
&lt;li&gt;Visual Studio Code supports selecting different versions of TypeScript.&lt;/li&gt;
&lt;li&gt;Visual Studio 2017/2019 have the SDK installers above, and MSBuild installs.&lt;/li&gt;
&lt;li&gt;Speed up Visual Studio Code startup time for large projects with a new mode for editors to provide a partial experience until the full language service experience has loaded up.&lt;/li&gt;
&lt;li&gt;Smarter auto-imports.&lt;/li&gt;
&lt;li&gt;There is now a new refactoring option which can convert code to utilise optional chaining.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Breaking Changes&lt;/h2&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#breaking-changes&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;
&lt;h3&gt;lib.d.ts Changes&lt;/h3&gt;
&lt;p&gt;Our lib.d.ts declarations have changed – most specifically, types for the DOM have changed.&lt;/p&gt;
&lt;h3&gt;Properties Overriding Accessors (and vice versa) is an Error&lt;/h3&gt;
&lt;p&gt;TypeScript now always issues an error when declaring a property in a derived class that would override a getter or setter in the base class.&lt;/p&gt;
&lt;h3&gt;Operands for delete must be optional&lt;/h3&gt;
&lt;p&gt;When using the delete operator in &lt;code class=&quot;language-text&quot;&gt;strictNullChecks&lt;/code&gt;, the operand must now be any, unknown, never, or be optional (in that it contains undefined in the type). Otherwise, use of the delete operator is an error.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;interface&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;prop&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; string&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;delete&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;prop&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;//     ~~~~~~&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// error! The operand of a &apos;delete&apos; operator must be optional.&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Usage of TypeScript&apos;s Node Factory is Deprecated&lt;/h2&gt;
&lt;p&gt;TypeScript 4.0 provides a new node factory API, the older &quot;factory&quot; functions for producing AST Nodes are now deprecated. &lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-4-0/#usage-of-typescripts-node-factory-is-deprecated&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official release note&lt;/a&gt; for more info.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Running client side only JavaScript with Gatsby.js]]></title><description><![CDATA[<p>Gatsby is great for generating static sites, but sometimes we need to run some client-side only JavaScript as well. The question is how might we go about that? Well, that's what I'll be covering in this blog post. Actually, I've already written about something similar in a recent post <a href="/render-client-side-only-component-in-next-js">Render client-side only component in Next.js</a>. The approach is very similar, but I'll be walking through an example in this blog that is more specific to Gatsby.</p>]]></description><link>https://blog.hao.dev/running-client-side-only-javascript-with-gatsby-js</link><guid isPermaLink="false">https://blog.hao.dev/running-client-side-only-javascript-with-gatsby-js</guid><pubDate>Wed, 12 Aug 2020 06:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Gatsby is great for generating static sites, but sometimes we need to run some client-side only JavaScript as well. The question is how might we go about that? Well, that&apos;s what I&apos;ll be covering in this blog post. Actually, I&apos;ve already written about something similar in a recent post &lt;a href=&quot;/render-client-side-only-component-in-next-js&quot;&gt;Render client-side only component in Next.js&lt;/a&gt;. The approach is very similar, but I&apos;ll be walking through an example in this blog that is more specific to Gatsby.&lt;/p&gt;&lt;h2&gt;What I tried to build&lt;/h2&gt;
&lt;p&gt;If you open up this blog on a mobile device or resize the window to a small enough width, you&apos;d see the sandwich menu button appear. The idea is that when this button is clicked, an overlay would appear along with the navigation links.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5z35Ysty1Et0l0E6zA9dH8/79f22a7ee4f757748c53d7e8cb81b7f8/Screenshot_2020-08-12_at_03.03.05.png&quot; alt=&quot;Mobile menu navbar&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/71XG5Q6dPn6x7OSqKBwnIW/8fe4e6e0cfe23b4ec4c1d8a088d74e3a/Screenshot_2020-08-12_at_03.03.13.png&quot; alt=&quot;Mobile menu overlay&quot;&gt;&lt;/p&gt;
&lt;p&gt;The challenge is that, if I implement this in React, then Gatsby will try to render out the logic into HTML. Which means after the site has been generated, clicking on the menu button will not do anything.&lt;/p&gt;
&lt;p&gt;There are two crucial factors to consider when trying to implement client-side only logic in Gatsby:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Delaying any logic during &quot;on-component-mount&quot; stage of the life cycle until we are on the client-side.&lt;/li&gt;
&lt;li&gt;We need to be careful with any &lt;code class=&quot;language-text&quot;&gt;window&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;document&lt;/code&gt; references that only exist on the client-side.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;I used &lt;code class=&quot;language-text&quot;&gt;useState&lt;/code&gt; to track the open/close state of the overlay. Since I wanted to disable the background scroll when the overlay is open as well, I ended up going with handler function &lt;code class=&quot;language-text&quot;&gt;handleMobileNavChange()&lt;/code&gt; to set the overflow value of &lt;code class=&quot;language-text&quot;&gt;&amp;lt;body&gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;p&gt;On clicking on the button, the value of &lt;code class=&quot;language-text&quot;&gt;mobileNavOpen&lt;/code&gt; will be set to true, and use can close the overlay by clicking on it which will set the state value to false.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;	
	&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;mobileNavOpen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setMobileNavOpen&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMobileNavChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overflow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;hidden&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;style&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overflow &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; originalStyle&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;setMobileNavOpen&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

	&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
		&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;button&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMobileNavChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;Sandwich menu&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
			&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Overlay&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;onClick&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleMobileNavChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
		&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The value of &lt;code class=&quot;language-text&quot;&gt;originalStyle&lt;/code&gt; is set outside of the Header component by reading the value from DOM. It was to ensure the correct overflow value is fetched on the initial page load.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;typeof window !== &apos;undefined&apos;&lt;/code&gt; check is to prevent Gatsby from erroring when generating the static site since window only exists on Browser and would error in Node environment.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; originalStyle &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt; window &lt;span class=&quot;token operator&quot;&gt;!==&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; window&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getComputedStyle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;overflow&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Header&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; Header&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I have simplified the example above to make the explanation a bit simpler, if you are interested here is the code on &lt;a href=&quot;https://github.com/h-dong/gatsby-blog/blob/master/src/components/Header.js&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixing Cypress errors part 3: miscellaneous and additional notes]]></title><description><![CDATA[<p>This is the last part of this blog series on Cypress errors. There were some additional notes I made which didn't go into the previous posts. This blog post is less specific and more about general advice and some obscure errors I saw along the way.</p>]]></description><link>https://blog.hao.dev/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes</link><guid isPermaLink="false">https://blog.hao.dev/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes</guid><pubDate>Thu, 30 Jul 2020 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This is the last part of this blog series on Cypress errors. There were some additional notes I made which didn&apos;t go into the previous posts. This blog post is less specific and more about general advice and some obscure errors I saw along the way.&lt;/p&gt;&lt;p&gt;Blog Series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes&quot;&gt;Fixing Cypress errors part 1: chromium out of memory crashes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-2-cannot-find-element-due-to-assertion-timing-out&quot;&gt;Fixing Cypress errors part 2: cannot find element due to assertion timing out&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is the last part of this blog series on Cypress errors. There were some additional notes I made which didn&apos;t go into the previous posts. This blog post is less specific and more about general advice and some obscure errors I saw along the way. &lt;/p&gt;
&lt;h2&gt;Are you using the latest of everything?&lt;/h2&gt;
&lt;p&gt;Cypress is being actively developed right now, in the span of 2 months, I saw six minor updates. It is very likely any issues that you see, assuming it was something Cypress team could address, then it&apos;ll get resolved very quickly.&lt;/p&gt;
&lt;p&gt;We were using &lt;code class=&quot;language-text&quot;&gt;cypress/included:4.5.0&lt;/code&gt; at the start, I updated it to &lt;code class=&quot;language-text&quot;&gt;cypress/included:4.10.0&lt;/code&gt; after reading a lot of bug fixes in the changelog. Unfortunately, upgrading to the latest version didn&apos;t help me much with my issues. However, if you are debugging a new issue that occurred on an exiting project, this might be the first thing to try. Cypress team provides an &lt;a href=&quot;https://docs.cypress.io/guides/references/changelog.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official changelog&lt;/a&gt;. So give that a read instead of blindly upgrading. Don&apos;t forget to watch out for those breaking changes!&lt;/p&gt;
&lt;h2&gt;Rare errors&lt;/h2&gt;
&lt;h3&gt;Test runner exited unexpectedly&lt;/h3&gt;
&lt;p&gt;I saw this error once or twice during my instigation (running over 100 builds). It wasn&apos;t obvious what caused the problem, and I wasn&apos;t able to reproduce it since. Maybe just something to be aware of that this kind of errors could occur.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3SznPHm8tQ3AHzYiXykdn3/8a2b3289f05fcd4e0e3e5e0eb05d9164/image.png&quot; alt=&quot;Cypress test runner exited unexpectedly&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Failed to connect to Chrome&lt;/h3&gt;
&lt;p&gt;This error seems to be caused by the fact Cypress wasn&apos;t able to connect to Chrome. I&apos;m not sure how this could happen when everything was running inside a Docker container on the CI pipeline.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/ZVDkZOP1mflRHukj3KJz3/6ae168394b1c4f589dff450b86cb2d7c/image.png&quot; alt=&quot;Cypress unable to connect to chrome error&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once again, I&apos;ve only seen this error once after switching over from Electron to Chrome, and the failure also went away after triggering a rerun.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixing Cypress errors part 2: cannot find element due to assertion timing out]]></title><description><![CDATA[<p>Apart from the Cypress chromium crashes I explained in <a href="/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes">Part 1</a> of this blog series, I also experienced some assertion timeout errors due to element cannot be found. In this part, we will talk about ways you could fix it.</p>]]></description><link>https://blog.hao.dev/fixing-cypress-errors-part-2-cannot-find-element-due-to-assertion-timing-out</link><guid isPermaLink="false">https://blog.hao.dev/fixing-cypress-errors-part-2-cannot-find-element-due-to-assertion-timing-out</guid><pubDate>Thu, 30 Jul 2020 14:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Apart from the Cypress chromium crashes I explained in &lt;a href=&quot;/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes&quot;&gt;Part 1&lt;/a&gt; of this blog series, I also experienced some assertion timeout errors due to element cannot be found. In this part, we will talk about ways you could fix it.&lt;/p&gt;&lt;p&gt;Blog Series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes&quot;&gt;Fixing Cypress errors part 1: chromium out of memory crashes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes&quot;&gt;Fixing Cypress errors part 3: miscellaneous and additional notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Error: Random timeout issues due to flaky tests&lt;/h2&gt;
&lt;p&gt;This problem was almost as common as the chromium crashing error. Cypress E2E tests would randomly error and complain it cannot find certain elements. The weird thing is that most of the time, all tests pass, and when tests fail, it is different every time. Finally, rerunning the build often fixes them, to the point people who maintain the project told me this is fine.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3W7LqyXKlc1A6kek4ahE9z/a41394b4543434488c5b4c9998de6477/image.png&quot; alt=&quot;Cypress timeout error&quot;&gt;&lt;/p&gt;
&lt;p&gt;Note: interestingly for me, the flaky tests stop after implementing the memory crashes mentioned in &lt;a href=&quot;/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes&quot;&gt;Part 1&lt;/a&gt;. So before you try any of these solutions, give that a try first.&lt;/p&gt;
&lt;h2&gt;Solution 1: Increase timeout time&lt;/h2&gt;
&lt;p&gt;Back in Selenium days, it was common to use commands like &lt;code class=&quot;language-text&quot;&gt;wait(10000)&lt;/code&gt; in between assertions to wait up to 10 seconds in case network was unstable or slow. It would reduce tests&apos; flakiness in a lot of situations.&lt;/p&gt;
&lt;p&gt;Cypress has a similar API &lt;code class=&quot;language-text&quot;&gt;cy.wait(500)&lt;/code&gt;, but we don&apos;t want to use it because it is an anti-pattern as explained by &lt;a href=&quot;https://docs.cypress.io/guides/references/best-practices.html#Unnecessary-Waiting&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Unnecessary Waiting - Cypress best practices&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Instead, we are looking at using a more elegant solution. Since Cypress already has a default timeout duration of &lt;code class=&quot;language-text&quot;&gt;5000ms&lt;/code&gt; (5s), sometimes we may need to extend the duration slightly without interfering too much with what Cypress is doing.&lt;/p&gt;
&lt;p&gt;We can do this by passing an extra argument to &lt;code class=&quot;language-text&quot;&gt;cy.get()&lt;/code&gt; API. Here is an example of how to do this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;cy&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;[data-testid=&quot;modal&quot;]&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;timeout&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;be.visible&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Of course, if you have timeouts all over the place as I mentioned above, then maybe, we need a more drastic approach and extend the global timeout.&lt;/p&gt;
&lt;p&gt;The simplest way to do this is by using the &lt;code class=&quot;language-text&quot;&gt;defaultCommandTimeout&lt;/code&gt; flag or in this case, updating it in the &lt;code class=&quot;language-text&quot;&gt;cypress.json&lt;/code&gt; config file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// cypress.json&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;baseUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;defaultCommandTimeout&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Personally, this didn&apos;t help much with the errors I saw. They still appeared just as frequently after I made these changes.&lt;/p&gt;
&lt;h2&gt;Solution 2: Retrying failed tests&lt;/h2&gt;
&lt;p&gt;The alternative to increasing the timeout duration is to run the failed tests again. Based on what I saw as part of my investigation, a lot of people were recommending this approach as well.&lt;/p&gt;
&lt;p&gt;The plugin is called &lt;code class=&quot;language-text&quot;&gt;cypress-plugin-retries&lt;/code&gt;, and it will do what the name suggests, rerun individual failed tests. After implementing this, the timeout errors all disappeared from the pipeline.&lt;/p&gt;
&lt;p&gt;The plugin&apos;s &lt;a href=&quot;https://github.com/Bkucera/cypress-plugin-retries&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;README&lt;/a&gt; has clear instructions on how to implement it so that I won&apos;t repeat it here.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;My conclusion is that solution 1 is worth trying if you always see the same tests failing due to assertion timeout. If the tests are failing randomly, I think solution 2 is a better bet. But as a colleague of mine correctly pointed out, these solution 2 doesn&apos;t remove the root problem in your application.&lt;/p&gt;
&lt;p&gt;Lastly, if you are still having issues, maybe check out the &lt;a href=&quot;/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes&quot;&gt;Part 1&lt;/a&gt; and &lt;a href=&quot;/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes&quot;&gt;Part 3&lt;/a&gt; of this blog series.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixing Cypress errors part 1: chromium out of memory crashes]]></title><description><![CDATA[<p>After recently working on an existing project that adopted Cypress, I was both amazed at how pleasant the local developer experience was and how badly it runs on the CI pipeline. No joke, at one point I actually missed Selenium. To debug the issue, I must have run the pipeline over 100 if not 200 times and read many many Github issue threads. Finally, I was able to problem resolved and noted down a few things along the way which may help someone else in the same shoes. I am pretty confident these solutions will fix your problems, if not leave an angry comment and let me know that I failed 😂</p>]]></description><link>https://blog.hao.dev/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes</link><guid isPermaLink="false">https://blog.hao.dev/fixing-cypress-errors-part-1-chromium-out-of-memory-crashes</guid><pubDate>Thu, 30 Jul 2020 09:00:00 GMT</pubDate><content:encoded>&lt;p&gt;After recently working on an existing project that adopted Cypress, I was both amazed at how pleasant the local developer experience was and how badly it runs on the CI pipeline. No joke, at one point I actually missed Selenium. To debug the issue, I must have run the pipeline over 100 if not 200 times and read many many Github issue threads. Finally, I was able to problem resolved and noted down a few things along the way which may help someone else in the same shoes. I am pretty confident these solutions will fix your problems, if not leave an angry comment and let me know that I failed 😂&lt;/p&gt;&lt;p&gt;Blog Series:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-2-cannot-find-element-due-to-assertion-timing-out&quot;&gt;Fixing Cypress errors part 2: cannot find element due to assertion timing out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes&quot;&gt;Fixing Cypress errors part 3: miscellaneous and additional notes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Error: Out of memory, chromium renderer crashed&lt;/h2&gt;
&lt;p&gt;Chromium crashing is one of the issues I kept seeing in the pipeline. It would happen even when no code was changed and with all the tests passing locally.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6xFA93SyQfRE86ZemHqAy7/804ef1132fe2f2bc91d97e9371708759/image.png&quot; alt=&quot;cypress crashing due to out of memory&quot;&gt;&lt;/p&gt;
&lt;p&gt;Cypress team was kind enough to give a very detailed error message in the console and even a link pointing to a potential fix. If you follow the link provided, you will find the same error description on Cypress&apos; official website and yet another link to click on which takes you on this &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/350&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github issue 350&lt;/a&gt;. A few pieces of advice were offered here on this thread, let&apos;s take a look at them separately.&lt;/p&gt;
&lt;h2&gt;Solution 1: Use --ipc=host Docker flag&lt;/h2&gt;
&lt;p&gt;The was a recommended fix from one of the Cypress authors.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is actually not indicative of a memory leak inside of Cypress (nor your application). This has to do with the way that browsers work under the hood. Luckily, we have a found a simple one line fix for this problem - simply add the flag &lt;code class=&quot;language-text&quot;&gt;--ipc=host&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;IPC stands for inter-process communication, and it is related to Docker networking. Docker allows a few different options and &lt;code class=&quot;language-text&quot;&gt;host&lt;/code&gt; is one of them. With this option, it is meant to enable the Docker container to use the host system’s IPC namespace. It seems to solve insufficient shared memory (shm) issues within worker (child containers). To be completely honest, it is a little over my head what the underlying issue is here.  My understanding is that this config allows the child container to utilise its parent&apos;s memory resources which would be more than its own, and thereby eliminating the problem.&lt;/p&gt;
&lt;p&gt;If networking is something you are interested or knows a lot about, maybe you will have better luck with the &lt;a href=&quot;https://docs.docker.com/engine/reference/run/#/ipc-settings---ipc&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Docker doc&lt;/a&gt;, which explains it this in more detail.&lt;/p&gt;
&lt;p&gt;So did this fix my problem? Nope. When I checked this workaround was already in place, but I was still getting those crashes reasonably consistently. But this might be worth trying if you do not have this in place already.&lt;/p&gt;
&lt;h2&gt;Solution 2: Use --disable-dev-shm-usage Cypress flag&lt;/h2&gt;
&lt;p&gt;If you continue to read in the comments in &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/350&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github issue 350&lt;/a&gt; you will see people suggest using &lt;code class=&quot;language-text&quot;&gt;--disable-dev-shm-usage&lt;/code&gt; flag with Cypress. &lt;/p&gt;
&lt;p&gt;When I decided to try this, I found out that the project already had this fix in place, but it was using the wrong config. It was using &lt;code class=&quot;language-text&quot;&gt;chrome browser family&lt;/code&gt; with Cypress v4 (see below for more description on why this is wrong). Unfortunately, even after correcting this mistake, the pipeline was still crashing. Which meant I had to continue my search.&lt;/p&gt;
&lt;p&gt;Given the number of positive emojis on the Github comment, it must have solved the problem for a lot of people. So don&apos;t let the fact it didn&apos;t work for me put you off, give this a try by reading how I tested this solution. below&lt;/p&gt;
&lt;p&gt;There are different ways to implement the same fix in Cypress, so we will talk about both here.&lt;/p&gt;
&lt;h3&gt;Using browser family&lt;/h3&gt;
&lt;p&gt;If you are using family property, make sure to use &lt;em&gt;chromium&lt;/em&gt; if you are using Cypress v4 or later. If you are using v3 or earlier then use &lt;em&gt;chrome&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;As of Cypress v4, the property now supports two options: &lt;em&gt;chromium&lt;/em&gt; or &lt;em&gt;firefox&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// cypress/plugins/index.js&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// v4&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;on&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;before:browser:launch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; launchOptions&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;family &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chromium&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      launchOptions&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--disable-dev-shm-usage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; launchOptions&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// v3&lt;/span&gt;
module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;on&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;before:browser:launch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;family &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chrome&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--disable-dev-shm-usage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; args&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There was a breaking change introduced in Cypress v4.0.0.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;We updated the Cypress browser objects of all Chromium-based browsers, including Electron, to have chromium set as their family field. Addresses #6243.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://docs.cypress.io/guides/references/changelog.html#4-0-0&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://docs.cypress.io/guides/references/changelog.html#4-0-0&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;See this &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/1519#issuecomment-586134264&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github comment&lt;/a&gt; on differences between v3 and v4.&lt;/p&gt;
&lt;h3&gt;Using browser name&lt;/h3&gt;
&lt;p&gt;Using browser name means you have to specify the exact browser name.&lt;/p&gt;
&lt;p&gt;This property supports these options: &lt;em&gt;chrome&lt;/em&gt;, &lt;em&gt;electron&lt;/em&gt;, &lt;em&gt;edge&lt;/em&gt; or &lt;em&gt;firefox&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;For example, if you wanted to use both &lt;em&gt;chrome&lt;/em&gt; and &lt;em&gt;electron&lt;/em&gt; then your snippet might look like this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// cypress/plugins/index.js&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;exports&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;on&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;before:browser:launch&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;browser &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; args&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chrome&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      args&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;--disable-dev-shm-usage&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;electron&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      args&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;disable-dev-shm-usage&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; args
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;PS: notice how Electron sets the argument slightly differently. There is no better reason other than Electron takes in options different to other browsers. See this &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/1519#issuecomment-451868889&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github comment&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;Solution 3: Change over from Electron to Chrome&lt;/h2&gt;
&lt;p&gt;So where do we stand right now? The project I was working on already had the first two fixes in place (although I had to fix the browser family). Just before I was losing hope, I saw &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/350#issuecomment-645580175&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this Github comment&lt;/a&gt; asking when the memory issue will be fixed for Electron.&lt;/p&gt;
&lt;p&gt;Could this be the issue?&lt;/p&gt;
&lt;p&gt;Since the project I was working on was already using the cypress/included docker image, this meant the Docker image included Chrome browser already.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cypress/included&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;4.10.0&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And I could simply use &lt;code class=&quot;language-text&quot;&gt;--headless --browser chrome&lt;/code&gt; flags to tell Cypress to use Chrome instead of Electron.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;cypress run --headless --browser chrome&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After pushing this change, I finally got some positive results!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5ccTS71A4baWDFEAGjW3ig/c5903a2fb818e1c20201073a212e9a17/image.png&quot; alt=&quot;E2E tests passing&quot;&gt;&lt;/p&gt;
&lt;p&gt;This was the first time where I was able to get the pipeline to pass 5 times in a row. The previous best score was 3 out of 5 runs. Of course, all subsequent runs have all been running fine as well!&lt;/p&gt;
&lt;h2&gt;Solution 4: Reduce the number of tests kept in memory&lt;/h2&gt;
&lt;p&gt;As mentioned already, I was able to address all of the pipeline issues I was seeing using the above solutions. This solution was something I discovered, tried out and didn&apos;t have much luck with it. I thought I&apos;d document it here anyway, in case it is the necessary fix for some people&apos;s problems.&lt;/p&gt;
&lt;p&gt;In a more recent (and still open in time of writing) &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/4164&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github issue 4164&lt;/a&gt; introduced me to this Cypress config called &lt;code class=&quot;language-text&quot;&gt;numTestsKeptInMemory&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You can set it in &lt;code class=&quot;language-text&quot;&gt;cypress.json&lt;/code&gt; file like this or pass it in as another flag.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// cypress.json&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;baseUrl&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8080/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;numTestsKeptInMemory&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The default value of &lt;code class=&quot;language-text&quot;&gt;numTestsKeptInMemory&lt;/code&gt; is 50. For some people reducing this number seemed to have helped them. The recommendation is to try with a low value such as &lt;code class=&quot;language-text&quot;&gt;1&lt;/code&gt;, and if that doesn&apos;t work then finally try &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Note: setting &lt;code class=&quot;language-text&quot;&gt;numTestsKeptInMemory&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;0&lt;/code&gt; will disable this feature altogether, and no tests will be kept in memory.&lt;/p&gt;
&lt;p&gt;There is the &lt;a href=&quot;https://docs.cypress.io/guides/references/configuration.html#Global&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt; for more information.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;CI pipeline issues such as this are very annoying to fix because it is not reproducible locally. Which means it requires pushing changes and wait for the pipeline to finish to test anything. I hope you managed to resolve your issue using one of the solutions mentioned here, if not try why out &lt;a href=&quot;/fixing-cypress-errors-part-2-cannot-find-element-due-to-assertion-timing-out&quot;&gt;Part 2&lt;/a&gt; and &lt;a href=&quot;/fixing-cypress-errors-part-3-miscellaneous-and-additional-notes&quot;&gt;Part 3&lt;/a&gt; of this blog series, hopefully, one of them contains your answers!&lt;/p&gt;
&lt;p&gt;Just a side point, as part of the research I found out about this &lt;a href=&quot;https://github.com/cypress-io/cypress/issues/349&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;open Github issue 349&lt;/a&gt; which is looking at introducing auto-recovery to Cypress which may continue running even after a crash. However, since it was opened back in 16th Dec 2016, therefore I wouldn&apos;t hold my breath for it to be done any time soon.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to approach a disagreement]]></title><description><![CDATA[<p>As software engineers, there are a lot of things to disagree on: tech stack, syntax, code standard, testing, methodologies, etc. No matter your seniority, learning how to frame a disagreement is vital. It includes both disagreeing with what others have done and have others disagree with your approach. It happens a lot more often than we expect, so if we don't pay attention to our methods, then it is easy to cause stress and tension within the team or between teams. If this continues for a long time then well, then it will create a toxic work environment. As someone who has a consultancy background, which requires people to jump in a new team and start delivering value as well as navigate office politics, I thought this is an intriguing subject for me to share my thoughts on.</p>]]></description><link>https://blog.hao.dev/how-to-approach-a-disagreement</link><guid isPermaLink="false">https://blog.hao.dev/how-to-approach-a-disagreement</guid><pubDate>Tue, 28 Jul 2020 17:59:00 GMT</pubDate><content:encoded>&lt;p&gt;As software engineers, there are a lot of things to disagree on: tech stack, syntax, code standard, testing, methodologies, etc. No matter your seniority, learning how to frame a disagreement is vital. It includes both disagreeing with what others have done and have others disagree with your approach. It happens a lot more often than we expect, so if we don&apos;t pay attention to our methods, then it is easy to cause stress and tension within the team or between teams. If this continues for a long time then well, then it will create a toxic work environment. As someone who has a consultancy background, which requires people to jump in a new team and start delivering value as well as navigate office politics, I thought this is an intriguing subject for me to share my thoughts on.&lt;/p&gt;&lt;h2&gt;When dishing out disagreements&lt;/h2&gt;
&lt;h3&gt;Are you raising a valid concern?&lt;/h3&gt;
&lt;p&gt;Sometimes, our emotional side can get to us, and we raise questions which aren&apos;t related or urgent. It is worth being mindful about the issues you are highlighting and reflect on it before speaking up. If it is already too late, then there is nothing wrong with a quick apology and retract your statement. It is as simple as saying something like &quot;actually, on second thought I am not sure it is relevant. Please ignore me&quot;.&lt;/p&gt;
&lt;p&gt;Having the gut to admit you are wrong is a good thing, and I, for one, appreciate this kind of behaviour over continue to argue even if you are not sure for argument&apos;s sake. It may depend on the company culture, but it is worth questioning if you should remain in a company that penalises you for making small mistakes like this.&lt;/p&gt;
&lt;p&gt;It is always better to do the reflection in your head before speaking up, and you don&apos;t want to be the person always asking questions and continuously retracting their questions in meetings.&lt;/p&gt;
&lt;h3&gt;Are you offering solutions?&lt;/h3&gt;
&lt;p&gt;There is nothing wrong with raising a lot of questions or issues, and instead, it should be encouraged. It doesn&apos;t mean the raised questions or concerns must be addressed straightaway. If they are things which can be taken care of later, then create a ticket and add it to the backlog for a later date.&lt;/p&gt;
&lt;p&gt;It is also essential to think about some potential solutions when raising issues. Not only does that show you have put thoughts into it, but the extra effort will make people less defensive since the conversation is more about picking the best solution at that point instead of the natural response to issues = who caused the problem.&lt;/p&gt;
&lt;p&gt;Compare the following statements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Statement 1: &quot;Some bad practices are being used in the codebase&quot;.&lt;/li&gt;
&lt;li&gt;Statement 2: &quot;Some new practices are being used in the codebase deviating from the standard. Should we consider adopting a linting tool going forward? I think Eslint is quite good, but I&apos;m sure there are others out there. Maybe we should have a spike on this?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Are you motivated to get it resolved?&lt;/h3&gt;
&lt;p&gt;It is often easy to point out problems with something others had done. When you raise a concern, in people&apos;s eyes, you are getting yourself involved (unless you are the CEO of the company). Just saying this isn&apos;t good enough is not only irresponsible but creates a &quot;you v.s. me&quot; situation, and implies &quot;see, I&apos;m better than you&quot; kind of message. Even if you didn&apos;t mean that it is easy for people to get that feeling, from a logical sense, if the issue you pointed out if actually that urgent, then maybe it makes sense for you to act on it as well and not just say it.&lt;/p&gt;
&lt;p&gt;Again, compare these statements:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Statement 1: &quot;I noticed the app error rate has increased by 200% over the last week&quot;. &lt;/li&gt;
&lt;li&gt;Statement 2: &quot;I noticed the app error rate has increased by 200% over the last week, who&apos;s the best person I should talk to about this? Happy to share my findings, support or pair on this as it is affecting the stability of the site.&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Lead by example is always a great way to build team rapport, and everyone will remember it (including your managers 😁).&lt;/p&gt;
&lt;h2&gt;When dealing with disagreements&lt;/h2&gt;
&lt;h3&gt;Be patient and professional&lt;/h3&gt;
&lt;p&gt;We all make mistakes, and we need to own up to them. Getting defensive is never a good look, nor is never admitting that you are wrong when the evidence is right in front of you. Being professional means being accountable to your actions, after all that is professional if everyone can go and do things and not worry about consequences? When people raise issues about your work or approach, don&apos;t cut people up mid-sentence. Allow everyone to express their feelings and thoughts, then have a healthy and calm discussion about it. It is possible that it was merely some misunderstanding, and after some talk, you both find you agree with each other. Some people might have different ways to express themselves, and others may even have language problems if they are not speaking in their mother-tone.&lt;/p&gt;
&lt;p&gt;Give people benefit of the doubt, and assume people are curious instead of attacking you or your work personally. Some people might speak fast or have a loud voice; in both situations, they might not even be aware of it and don&apos;t mean harm. So don&apos;t react too quickly, and you&apos;ll avoid awkward apologies after.&lt;/p&gt;
&lt;p&gt;If you are sure someone is rude or aggressive towards you, then consider giving them feedback or speaking with them in private before raising it with the right channels (e.g. manager, HR, etc.). It could just be that person is having an awful day and not being themselves.&lt;/p&gt;
&lt;h3&gt;Be able to take criticisms&lt;/h3&gt;
&lt;p&gt;Some criticisms might be misjudged, but it is always important to reflect on feedback and think about what may have triggered the other person to feel a certain way or say what they said. If someone was questioning code quality was it because how you structured your code or how you named everything which made it hard to understand? Just because you are not in the wrong, it doesn&apos;t mean there is no room for improvement.&lt;/p&gt;
&lt;p&gt;We should welcome critiques, only by knowing what we are doing wrong can we improve the next time. Some even regard feedback, positive or constructive, as a gift. Also, if it is not something you value too much about, the fact the other person wants to open up to you and tell you what they think or how they feel is huge. It shows that they value you as a teammate. After all, would you go out of your way and give critiques to a stranger?&lt;/p&gt;
&lt;p&gt;Giving good feedback is difficult, and many people avoid giving criticisms because they don&apos;t feel comfortable for one reason or another. As a result, it is probably worth valuing what critiques you do receive.&lt;/p&gt;
&lt;p&gt;Finally, just want to say being able to take in criticism isn&apos;t the same as accepting abuse. I doubt this will happen to you, but just something to be aware of in the back of your mind. Again, always give people benefit of the doubt when unsure!&lt;/p&gt;
&lt;h3&gt;Admit mistakes&lt;/h3&gt;
&lt;p&gt;No one likes to be told they are wrong, and wouldn&apos;t it be wonderful never to make mistakes? Unfortunately, that is not possible or realistic. As a responsible professional, we need to stand by the decisions we&apos;ve made and more importantly admit we are wrong when presented the facts.&lt;/p&gt;
&lt;p&gt;Is it painful to do that? Yes, absolutely. But that is not the reason to shy away from it. Painful experiences are the ones we remember, and this is how we learn and improve the next time. The main take away is to remember not to get yourself into the same situation the next time. Did you over-promise something or were too sure of yourself? Maybe next time leave some room for manoeuvre when making a claim. Was your work not up to standard? Maybe next time get someone to check over it before putting it in front of everyone.&lt;/p&gt;
&lt;p&gt;The best way to deflect the shame and regret that comes from mistakes is to be the first one to point out that you made a mistake. By doing this, not only do you avoid someone else wording the situation that&apos;s less in your favour, but also takes the worrying out of the equation. The worst part about making a mistake is thinking &quot;Oh god, when will people find out, and what will they do or think?&quot;. By openly stating your mistake, you will eliminate that worry.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;Unable to have a productive discussion due to fear of confrontations or lack some communication skills is something many people struggle with from my experience. It had made me appreciate even more the &quot;soft-skills training&quot; I received back when I was working for a consultancy.&lt;/p&gt;
&lt;p&gt;In a lot of ways, it is all a mindset difference, but it is harder than it sounds because people have often built the mindset into their ways of working and styles of communication. These points I mentioned aren&apos;t tricky to do, just requires some self-reflection to identify how and where you need to improve. Or even better, ask for others for feedback!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to render react components in markdown in Gatsby]]></title><description><![CDATA[<p>In a recent post <a href="https://haodong.io/stop-trying-you-can-t-multitask" target="_blank" rel="nofollow noopener noreferrer">Stop trying, you can't multitask</a>, I wanted to embed a few productivity books which I found useful. Since I write all the content in Markdown, I thought about embedding iframe components or insert image version of the book components in the Markdown, which are both terrible ideas. Luckily, there's a better way to do this with MDX, which allowed me to use React components inside Markdown. The solution is super clean, and in this article, I'll be explaining how to add React components to your Gatsby site.</p>]]></description><link>https://blog.hao.dev/how-to-render-react-components-in-markdown-in-gatsby</link><guid isPermaLink="false">https://blog.hao.dev/how-to-render-react-components-in-markdown-in-gatsby</guid><pubDate>Mon, 20 Jul 2020 18:37:00 GMT</pubDate><content:encoded>&lt;p&gt;In a recent post &lt;a href=&quot;https://haodong.io/stop-trying-you-can-t-multitask&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Stop trying, you can&apos;t multitask&lt;/a&gt;, I wanted to embed a few productivity books which I found useful. Since I write all the content in Markdown, I thought about embedding iframe components or insert image version of the book components in the Markdown, which are both terrible ideas. Luckily, there&apos;s a better way to do this with MDX, which allowed me to use React components inside Markdown. The solution is super clean, and in this article, I&apos;ll be explaining how to add React components to your Gatsby site.&lt;/p&gt;&lt;h2&gt;Install Gatsby plugins&lt;/h2&gt;
&lt;p&gt;There are two things we need to get this to work:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Gatsby needs to understand the React component when parsing the Markdown document. I used &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-component-parent2div&lt;/code&gt; to detect custom components.&lt;/li&gt;
&lt;li&gt;Gatsby needs to hydrate the React component. &lt;code class=&quot;language-text&quot;&gt;rehype-react&lt;/code&gt; is required to transform HTML to React component.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Now install the following dependencies:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm i gatsby-transformer-remark rehype-react gatsby-remark-component-parent2div&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Why I use gatsby-remark-component-parent2div&lt;/h3&gt;
&lt;p&gt;Initially, I tried using &lt;code class=&quot;language-text&quot;&gt;gatsby-remark-component&lt;/code&gt; plugin. However, I found that it was throwing warnings in the console. It turns out Contentful, the headless CMS I am using, returns &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&gt;&lt;/code&gt; inside &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&gt;&lt;/code&gt;. It is against HTML specification so that some browsers will throw the warning. There are two ways to resolve this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Change the component, avoid using &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&gt;&lt;/code&gt; inside &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Use gatsby-remark-component-parent2div plugin instead, which changes the AST node parent of your custom component from &lt;code class=&quot;language-text&quot;&gt;&amp;lt;p&gt;&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;&amp;lt;div&gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The first method doesn&apos;t work for me since I cannot control how Contentful save Markdown into HTML. In the end, I went with the second method, which arguably is the easier approach out of the two.&lt;/p&gt;
&lt;p&gt;If you don&apos;t have this issue, I suggest you use &lt;code class=&quot;language-text&quot;&gt;use gatsby-remark-component&lt;/code&gt; plugin instead.&lt;/p&gt;
&lt;h2&gt;Update gatsby-config.js&lt;/h2&gt;
&lt;p&gt;If you want the ability to auto-detect custom components, then the config update is minimal.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gatsby-transformer-remark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;gatsby-remark-component-parent2div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I wanted to be somewhat defensive with my approach, incase in the future, there&apos;s a use case for non-React custom components.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gatsby-transformer-remark&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;gatsby-remark-component-parent2div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;book-affiliate-link&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                &lt;span class=&quot;token literal-property property&quot;&gt;verbose&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Update Markdown template&lt;/h2&gt;
&lt;p&gt;This step will compare the tag name of each element in the abstract syntax tree (AST), which is returned by GraphQL. If the element tag matches against the component map (in this case it&apos;s &lt;code class=&quot;language-text&quot;&gt;book-affiliate-link&lt;/code&gt; ) then it will to hydrate it with &lt;code class=&quot;language-text&quot;&gt;BookAffiliateLink&lt;/code&gt; React component.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; rehypeReact &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rehype-react&quot;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; MyComponent &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;../pages/my-component&quot;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderAst &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;rehypeReact&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;createElement&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;components&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string-property property&quot;&gt;&quot;book-affiliate-link&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; BookAffiliateLink &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Compiler&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Replace &lt;code class=&quot;language-text&quot;&gt;dangerouslySetInnerHTML&lt;/code&gt; with the &lt;code class=&quot;language-text&quot;&gt;renderAst&lt;/code&gt; function we introduced above because we want to render out the custom component in React instead of static HTML.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// &amp;lt;div dangerouslySetInnerHTML={{ __html: post.html }} /&gt;&lt;/span&gt;
&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;renderAst&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;htmlAst&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Update GraphQL query&lt;/h2&gt;
&lt;p&gt;The final step is to update the GraphQL query, so we get the result in the correct (AST) form.&lt;/p&gt;
&lt;p&gt;Previously, I was querying for the Markdown like this.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;body &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  childMarkdownRemark &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    html
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;You could replace &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; with &lt;code class=&quot;language-text&quot;&gt;htmlAst&lt;/code&gt;, however, since I need &lt;code class=&quot;language-text&quot;&gt;html&lt;/code&gt; will do a word count I now query both.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;body &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  childMarkdownRemark &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    htmlAst
    html
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Final result&lt;/h2&gt;
&lt;p&gt;Now when I add the following into my Markdown document.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;book-affiliate-link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Getting Things Done&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;subtitle&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;The Art of Stress-Free Productivity&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;author&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;David Allen&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&amp;lt;https://amzn.to/30WuPNm&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;imgsrc&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&amp;lt;https://images-na.ssl-images-amazon.com/images/I/41iI4SMqzCL._SX318_BO1,204,203,200_.jpg&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;book-affiliate-link&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The following book component will appear.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3YrGZnpeYMfydQNM0pT0ka/63e7aa91f4e3ee4eb7b4c9e1ca535f03/image.png&quot; alt=&quot;Book Affiliate Link Component&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Render client-side only component in Next.js]]></title><description><![CDATA[<p>If you are familiar with Next.js then you will know it is the React SSR (server-side rendering) framework created by Vercel. There are a lot of headaches in trying to build SSR sites in React, Next.js makes it drastically simpler by doing many of the not-so-nice parts and hiding it from developers. The problem with frameworks, in general, is that it does what it is designed to do very well while sacrifices control and customisation to achieve it. Next.js is very good at handling components rendered on server-side and then sent to the client. But how would you render a client-side only component in Next.js? That is an issue I encountered and what I'll be discussing in this article.</p>]]></description><link>https://blog.hao.dev/render-client-side-only-component-in-next-js</link><guid isPermaLink="false">https://blog.hao.dev/render-client-side-only-component-in-next-js</guid><pubDate>Tue, 30 Jun 2020 23:31:00 GMT</pubDate><content:encoded>&lt;p&gt;If you are familiar with Next.js then you will know it is the React SSR (server-side rendering) framework created by Vercel. There are a lot of headaches in trying to build SSR sites in React, Next.js makes it drastically simpler by doing many of the not-so-nice parts and hiding it from developers. The problem with frameworks, in general, is that it does what it is designed to do very well while sacrifices control and customisation to achieve it. Next.js is very good at handling components rendered on server-side and then sent to the client. But how would you render a client-side only component in Next.js? That is an issue I encountered and what I&apos;ll be discussing in this article.&lt;/p&gt;&lt;h2&gt;How to make code run only on client-side with Next.js?&lt;/h2&gt;
&lt;p&gt;To my surprise, this is surprisingly easy. Next.js inherited the &lt;code class=&quot;language-text&quot;&gt;process.browser&lt;/code&gt; check from Webpack. So the following check is all we need to run client-only code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;browser&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// on browser&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;What about client-side components?&lt;/h2&gt;
&lt;p&gt;Since it is just React, surely we can do the same check before rendering the client-side only component right?&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;browser &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyAwesomeComponent&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;some other component&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Well, yes and no. If I try to run the code above, the app will function normally, but React will throw this error in the browser console &lt;code class=&quot;language-text&quot;&gt;Warning: Expected server HTML to contain a matching &amp;lt;div&gt; in &amp;lt;div&gt;&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;Here&apos;s the complete warning:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2sS2r50mUHxCda4kIkIzvi/c96332faa6e9184d4a67b9036940e55f/Screenshot_2020-06-30_at_23.38.46.png&quot; alt=&quot;React hydration error&quot;&gt;&lt;/p&gt;
&lt;p&gt;Looking at the error itself, it is not too obvious what is going on at first. Don&apos;t worry, it is actually quite simple.&lt;/p&gt;
&lt;p&gt;In the example above, I&apos;ve told React not to render anything on the server-side, so it didn&apos;t. When the code got handed over to the browser, React suddenly recognised the fact something needs to be rendered, and it does just that on the client-side. Since React expects the rendered content to be identical on both the server-side and the client-side, I have now confused it by breaking that expectation. So it then returns the favour and displays a confusing error that had me scratching my head for hours.&lt;/p&gt;
&lt;h2&gt;The solutions&lt;/h2&gt;
&lt;p&gt;I managed to narrow down the problem to &quot;something is causing React to go ape s**t during render and hydration&quot;. But I haven&apos;t gotten any further than that. Later, I spoke to a colleague and he pointed out how simple the solution was.&lt;/p&gt;
&lt;h3&gt;Solution 1 - suppressHydrationWarning&lt;/h3&gt;
&lt;p&gt;I felt pretty embarrassed, because all I had to do was add &lt;code class=&quot;language-text&quot;&gt;suppressHydrationWarning={true}&lt;/code&gt; attribute to the component with the issue.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;suppressHydrationWarning&lt;/span&gt;&lt;span class=&quot;token script language-javascript&quot;&gt;&lt;span class=&quot;token script-punctuation punctuation&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;browser &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MyAwesomeComponent&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;some other component&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token plain-text&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is worth noting that, this is merely hiding the error and the issue never went away. Make sure you are aware of the following if you decide to go with this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It only works one level deep (not a problem in this case)&lt;/li&gt;
&lt;li&gt;This should be avoided if at all possible, only to be used as last resort - me paraphrasing the React team&lt;/li&gt;
&lt;li&gt;All the patching could impact site performance in some scenarios&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Solution 2 - two-pass rendering&lt;/h3&gt;
&lt;p&gt;This is the recommended approach by the React team. As long as the DOM elements stay the same during the first pass render, it doesn&apos;t bug us with the error we saw above. To trigger a second pass, we will need to implement &lt;code class=&quot;language-text&quot;&gt;useEffect()&lt;/code&gt; React Hook and achieve a similar result as &lt;code class=&quot;language-text&quot;&gt;componentDidMount()&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;jsx&quot;&gt;&lt;pre class=&quot;language-jsx&quot;&gt;&lt;code class=&quot;language-jsx&quot;&gt;&lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// update some client side state to say it is now safe to render the client-side only component&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;renderClientSideComponent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Decision&lt;/h2&gt;
&lt;p&gt;In my team, we ended up going for the first solution since it is more straightforward, the client-side only component was a single level deep, and there wasn&apos;t any crazy logic which may cause performance issues.&lt;/p&gt;
&lt;h3&gt;Additional Reading&lt;/h3&gt;
&lt;p&gt;Here is the &lt;a href=&quot;https://reactjs.org/docs/react-dom.html#hydrate&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;original React documentation&lt;/a&gt; if you want to read more about it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Stop trying, you can't multitask]]></title><description><![CDATA[<p>There are a lot of articles out there discussing why people need to focus instead of trying to multitask. At best, I have seen this being done from a biology point of view going into depth about how our brain works on a more fundamental level. I studied Computer Science at University, and this multitasking problem isn't new to me. Let's discuss how computer operating systems schedule tasks and what valuable lessons we can learn from it.</p>]]></description><link>https://blog.hao.dev/stop-trying-you-can-t-multitask</link><guid isPermaLink="false">https://blog.hao.dev/stop-trying-you-can-t-multitask</guid><pubDate>Mon, 29 Jun 2020 21:36:00 GMT</pubDate><content:encoded>&lt;p&gt;There are a lot of articles out there discussing why people need to focus instead of trying to multitask. At best, I have seen this being done from a biology point of view going into depth about how our brain works on a more fundamental level. I studied Computer Science at University, and this multitasking problem isn&apos;t new to me. Let&apos;s discuss how computer operating systems schedule tasks and what valuable lessons we can learn from it.&lt;/p&gt;&lt;h2&gt;What is Process Scheduling?&lt;/h2&gt;
&lt;p&gt;A computer operating systems&apos; fundamental role is to allow users to interact with the many applications on its platform. While it can offer computer programs many benefits such as a natural and consistent way to display something visually, but more importantly avoid headaches that come from interacting with hardware directly.&lt;/p&gt;
&lt;p&gt;At the early days, there is only one core (which includes one thread) on a CPU. The operating system has no choice but to run one application at one time. This was fine when everyone was inputting into a terminal and looking at a black screen. When operating systems introduced the GUI (Graphical User Interface), this was no longer possible, people naturally people wanted to interact with several programs at the same time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7FgX0KWD5b2nf1bBhw4iN6/b2d00bd98c8188a38bcd07e86c42996a/image.png&quot; alt=&quot;Process scheduler diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;From a hardware point of view, it was not possible to achieve multitasking. So computer scientists decided to tackle it via the software side. Then process schedulers came and saved the day! Instead of the CPU working on a single program at one time until the user decided to quit it, the operating system would instruct it to split its processing power across all applications that are running. By jumping around and processing different programs for an allocated amount of time, it provided the illusion that we are running many programs at the same time.&lt;/p&gt;
&lt;p&gt;However, it is not all rainbows and roses. The more operating system tries to switch between tasks, the less processing it could complete in the allocated time. In order to switch to another scheduled job, the current work progress has to be saved, and then new task data has to be loaded before it is possible to process anything. Both saving and loading take time, if the scheduled time for each task is too short, then the computer barely has enough time for anything else. Effectively, it will just be switching between tasks and doing zero processing, for a user it may feel like the computer has frozen and is unresponsive.&lt;/p&gt;
&lt;h2&gt;We are not so different from computers&lt;/h2&gt;
&lt;p&gt;The human brain works surprisingly, similar to a computer CPU. Our brain cannot process multiple pieces of data at the same time. The fact people think they could watching Netflix and doing work at the same time is also just an illusion. Behind the scenes, the brain is jumping between consuming video and audio from the TV screen and trying to think about the work problem they are trying to solve. It will not only exhaust you mentally very quickly, but it is also very ineffective.&lt;/p&gt;
&lt;p&gt;Just like the process scheduling, our brain needs time to switch between different tasks. It is usually referred to as context switching overhead/cost. No matter how trivial another task is, breaking your current flow will require energy and time to bring the focus back to what you were doing. To put simply, this context switching creates processing overhead and is very expensive. If constant interrupted, you would probably find yourself getting very little done even though you have been staring at your work for a very long time. Also, if you do manage to get the job done, the quality of work produced is likely to be far from your best when you have to &quot;mentally juggling&quot; multiple things.&lt;/p&gt;
&lt;p&gt;To be honest, I am not a biologist, so I won&apos;t be able to explain the intricacies of how the brain functions. But checkout this excellent video by BBC Ideas &lt;a href=&quot;https://www.youtube.com/watch?v=tMiyzuO1qMs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;What multitasking does to your brain&lt;/a&gt; which explained it perfectly.&lt;/p&gt;
&lt;p&gt;Here are a couple fun and easy to read books if you would like to learn more about the human brain.&lt;/p&gt;
&lt;div&gt;&lt;book-affiliate-link title=&quot;Idiot Brain&quot; subtitle=&quot;What Your Head Is Really Up To&quot; author=&quot;Dean Burnett&quot; link=&quot;https://amzn.to/2ZpcYfD&quot; imgsrc=&quot;https://images-na.ssl-images-amazon.com/images/I/41k0L8xUNkL._SX331_BO1,204,203,200_.jpg&quot;&gt;&lt;/book-affiliate-link&gt;&lt;/div&gt;
&lt;div&gt;&lt;book-affiliate-link title=&quot;The Chimp Paradox&quot; subtitle=&quot;What Your Head Is Really Up To&quot; author=&quot;Steve Peters&quot; link=&quot;https://amzn.to/3dMhKJ7&quot; imgsrc=&quot;https://images-na.ssl-images-amazon.com/images/I/5199B3+S95L._SY344_BO1,204,203,200_.jpg&quot;&gt;&lt;/book-affiliate-link&gt;&lt;/div&gt;
&lt;p&gt;(Please support me by using the affiliate book links 😁) &lt;/p&gt;
&lt;h2&gt;How to be mono-focused (one task at a time)&lt;/h2&gt;
&lt;h3&gt;1. Record ideas that are bothering you&lt;/h3&gt;
&lt;p&gt;Write down all the ideas floating in your head on a piece paper and promise yourself that you will come back to it later. Knowing these ideas will be reviewed at some point shortly, this will be enough to calm your brain down, and it&apos;ll feel &quot;safe&quot; to stop thinking about them allowing you to concentrate on your task fully. If you want to learn more about this, it is a technique I learned from Getting Things Done book.&lt;/p&gt;
&lt;div&gt;&lt;book-affiliate-link title=&quot;Getting Things Done&quot; subtitle=&quot;The Art of Stress-Free Productivity&quot; author=&quot;David Allen&quot; link=&quot;https://amzn.to/30WuPNm&quot; imgsrc=&quot;https://images-na.ssl-images-amazon.com/images/I/41iI4SMqzCL._SX318_BO1,204,203,200_.jpg&quot;&gt;&lt;/book-affiliate-link&gt;&lt;/div&gt;
&lt;h3&gt;2. Avoid visual and audio distractions&lt;/h3&gt;
&lt;p&gt;I often listen to music when trying to focus, but you mustn&apos;t overshoot it. In general, I would stay away from music videos because the adverse effect is too obvious to ignore. Slower songs with more lucid lyrics are also bad for me because my brain will start picking up on the cues. Instrumental music is my goto music genre for better concentration.&lt;/p&gt;
&lt;p&gt;Alternatively, if you don&apos;t want to listen to loud music, how about some peaceful rain noise? It keeps me calm and doesn&apos;t feed my brain too many triggers to go off on a tangent. I have been using &lt;a href=&quot;https://rain.today&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;rain.today&lt;/a&gt; a lot recently.&lt;/p&gt;
&lt;h3&gt;3. Avoid notifications&lt;/h3&gt;
&lt;p&gt;Silence every alerts on all devices when you are trying to focus. Don&apos;t let an unexpected message or notification from some random app break your previous focus and flow. If you struggle at the start, use a Pomodoro timer and follow its work and break schedule. Once you get comfortable with the idea, stop using a timer and just put your head down and work until you are mentally tired, then get up and walk around.&lt;/p&gt;
&lt;h3&gt;4. Noise cancellation headphones&lt;/h3&gt;
&lt;p&gt;Use a pair of noise cancellation headphones. I absolutely love using this type of headphones when I want to focus, and sometimes I would even use it for the noise cancellation without playing things. I find it that it could block most of the background hums, cracks, car driving past, distant chatter, etc.&lt;/p&gt;
&lt;h3&gt;5. Find people-free time&lt;/h3&gt;
&lt;p&gt;I am a night owl, so I focus very well late at night, this is probably because everyone else is asleep. It is a bit hard to do during the day or in a work setting, but try to find ways to find some quiet time by booking a small meeting room and work in there for a couple of hours if you have that luxury.&lt;/p&gt;
&lt;p&gt;Given the current Covid-19 situation, the distraction probably comes from family and flatmates. If you are struggling, it might be beneficial to have a calm and friendly chat with them to see if they could give you some space and time during the day. It might involve not disturbing you during a specific time of the day unless it is very urgent. &lt;/p&gt;
&lt;h2&gt;Additional Reading&lt;/h2&gt;
&lt;p&gt;Scheduling is an interesting algorithmic problem, people have designed many different scheduling methods over the years, although none is best at everything. Some are better at bursts of computation, while others may consume less energy but translates to a sluggish performance. There are many schedulers out there and check it out here &lt;a href=&quot;https://www.geeksforgeeks.org/process-schedulers-in-operating-system&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Process schedulers in operating system&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Set up domain forward from Cloudflare to Netlify]]></title><description><![CDATA[<p>I recently migrated a few domains to Cloudflare. Sure enough, my domain set up is all over the place afterwards. I decided to clean everything up and set up everything again from scratch. This article explains how I was able to get Cloudflare to forward all request to Netlify.</p>]]></description><link>https://blog.hao.dev/set-up-domain-forward-from-cloudflare-to-netlify</link><guid isPermaLink="false">https://blog.hao.dev/set-up-domain-forward-from-cloudflare-to-netlify</guid><pubDate>Mon, 15 Jun 2020 23:15:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently migrated a few domains to Cloudflare. Sure enough, my domain set up is all over the place afterwards. I decided to clean everything up and set up everything again from scratch. This article explains how I was able to get Cloudflare to forward all request to Netlify.&lt;/p&gt;&lt;p&gt;There is a much easier way of achieving what I am trying to do here with Nameservers, however, with Cloudflare you need to be at least on a business subscription for a given domain before they open up that feature to you.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5y4lskOOjGt8sfQ34242S7/f1156aa68857e5ae6db965b077f591bd/image.png&quot; alt=&quot;Cloudflare nameserver requirement&quot;&gt;&lt;/p&gt;
&lt;p&gt;How much does it cost to be on their business subscription I hear you ask?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3IKvejwwZnIsgqoMKc3eVK/2a92ef78c0bf8af9fa54f3faf963cad8/image.png&quot; alt=&quot;Cloudflare pricing&quot;&gt;&lt;/p&gt;
&lt;p&gt;That&apos;s right, $200 per month!&lt;/p&gt;
&lt;p&gt;Here I am with my blog that has no monetisation whatsoever. Clearly, it isn&apos;t the solution I can afford. Luckily, Cloudflare is &quot;kind enough&quot; to let you set up DNS records.&lt;/p&gt;
&lt;h2&gt;Add a custom domain on Netlify&lt;/h2&gt;
&lt;p&gt;Go to &quot;Domain management&quot; section on your Netlify app, then click on the big button that says &quot;Add custom domain&quot;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4dpJakXEOJDszbtPRHpMGo/384edf2fcf8acd94004d28fb5f733e1b/image.png&quot; alt=&quot;Netlify domain management&quot;&gt;&lt;/p&gt;
&lt;p&gt;Next, it should take to you to the following page. Add in your domain name here. In this example, I&apos;m using &quot;beta.haodong.io&quot;, but I could be using &quot;haodong.io&quot; if I was setting it up for my main blog.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2RMRWJQYf1zwWGWBHGlJT4/2270708e34223e96aa5ec54e740502d0/image.png&quot; alt=&quot;Netlify add domain&quot;&gt;&lt;/p&gt;
&lt;p&gt;After clicking on &quot;Verify&quot; button, it will state that the domain already has an owner. This is expected since the domain is now Cloudflare. Just click on &quot;Yes, add domain&quot; and continue to the next step.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3fDe8UhQ1elhOCoVrYCHvE/43ba6c5b042652eb36de54e14c2dc8d9/image.png&quot; alt=&quot;Netlify verify domain&quot;&gt;&lt;/p&gt;
&lt;p&gt;Now you can see &quot;beta.haodong.io&quot; has been added to the custom domain section, although it is greyed out.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/422cuzCh7erA2ZLFkM0mhG/4a13f50da0eedb37ed95e7704b0cde6c/image.png&quot; alt=&quot;Netlify custom domain added&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click on &quot;Check DNS configuration&quot; link, and then you should be presented with the following modal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1kn5VRaiwrPUrFNkt3hxav/3d94cbe6e44760201e11497766652344/image.png&quot; alt=&quot;Netlify DNS configuration&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;beta CNAME infallible-bhaskara-ddc8cd.netlify.app&lt;/code&gt; is the DNS record we need to add to our domain on Cloudflare.&lt;/p&gt;
&lt;h2&gt;Add DNS record on Cloudflare&lt;/h2&gt;
&lt;p&gt;Click on the &quot;DNS&quot; button in the top navigation bar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2GLkCfItoGHxJpPZQrSayk/da24081147b3c161de002dace0f3a72a/image.png&quot; alt=&quot;Cloudflare top nav bar&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then you should see the current DNS records, I&apos;ve greyed out mine just in case.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6zA777kIUXKhOImDQ8z8wz/64122a48cbd49ea3923b04cab5b6adb5/image.png&quot; alt=&quot;Cloudflare DNS records&quot;&gt;&lt;/p&gt;
&lt;p&gt;Click on &quot;+ Add record&quot; then you should see the following form dropping down. I have added the record Netlify told me to, and you should do the same since you use case might be slightly different.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3ESphblERzkmaM8bFSaqo/fb0b7b901472381c4a8954ef6f831590/image.png&quot; alt=&quot;Cloudflare add DNS record&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then wait for a few minutes (for me it was seconds), go to your domain and it should now work as expected.&lt;/p&gt;
&lt;h2&gt;Things to note&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Remove domains from Netlify altogether if you previously used Netlify DNS for the domain. Otherwise, it will get very confusing when attempting to debug any problems. You will confuse Netlify as well since now it will try to generate SSL/TLS certificate for your domain.&lt;/li&gt;
&lt;li&gt;As mentioned at the start of this article, you should try to use Servername and point to Netlify DNS preferably. The approach explained in this article is hackier, in my opinion. Netlify DNS has all the benefits of Cloudflare in terms of optimising assets, caching optimisation, post-processing, etc. If you are like me, who have different apps running on separate subdomains, then it is a lot easier to manage if Netlify manages the DNS.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Are you ready for Google Core Web Vitals?]]></title><description><![CDATA[<p>Google recently announced the new Core Web Vitals, which will reshape the web soon. Although now they are only used as guidance metrics for websites to follow, it will massively impact how Google's Search Engine algorithm works and thus, how websites get ranked on Google search results next year. Is your company or your site ready for all this? In this article, we will go over what these metrics are and how it affects developers and businesses.</p>]]></description><link>https://blog.hao.dev/are-you-ready-for-google-core-web-vitals</link><guid isPermaLink="false">https://blog.hao.dev/are-you-ready-for-google-core-web-vitals</guid><pubDate>Wed, 10 Jun 2020 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Google recently announced the new Core Web Vitals, which will reshape the web soon. Although now they are only used as guidance metrics for websites to follow, it will massively impact how Google&apos;s Search Engine algorithm works and thus, how websites get ranked on Google search results next year. Is your company or your site ready for all this? In this article, we will go over what these metrics are and how it affects developers and businesses.&lt;/p&gt;&lt;h2&gt;What is Core Web Vitals?&lt;/h2&gt;
&lt;p&gt;Google isn&apos;t happy the website standard today. It believes too many sites have poor user experience and performance. For good or for worse, Google has released the next step of their plan to drive the industry to up their game - Core Web Vitals.&lt;/p&gt;
&lt;p&gt;I thought the name on its own is quite self-explanatory. It will act as the key vitals (metrics) in the future when deciding if a website is healthy or not by Google&apos;s standards.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Web Vitals is an initiative by Google to provide unified guidance for quality signals that are essential to delivering a great user experience on the web. They are a set of real-world, user-centred metrics that quantify key aspects of the user experience. They measure dimensions of web usability such as load time, interactivity, and the stability of content as it loads. - Google&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It is worth mentioning that this isn&apos;t the first time Google has given websites a list of standards to follow. In the past, it has already punished sites that do not align with its vision in these areas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/08/helping-users-easily-access-content-on.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Mobile-friendliness&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/09/more-safe-browsing-help-for-webmasters.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Safe-browsing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/11/heres-to-more-https-on-web.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;HTTPS-security&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2016/08/helping-users-easily-access-content-on.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Intrusive interstitial guidelines&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Now, Google wants to add Core Web Vitals to the list.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7fstpYEBsj4rrNrWwCNFAn/49516a5a06307836d63337ff435a7796/image.png&quot; alt=&quot;Core Web Vital Metrics&quot;&gt;&lt;/p&gt;
&lt;p&gt;To show they are serious; it even gave thresholds for metrics in terms of how performance will be measure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/lcp/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Largest Contentful Paint (LCP)&lt;/a&gt;&lt;/strong&gt;: measures loading performance. To provide a good user experience, LCP should occur within &lt;strong&gt;2.5 seconds&lt;/strong&gt; of when the page first starts loading.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/fid/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;First Input Delay (FID)&lt;/a&gt;&lt;/strong&gt;: measures interactivity. To provide a good user experience, pages should have a FID of less than &lt;strong&gt;100 milliseconds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href=&quot;https://web.dev/cls/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cumulative Layout Shift (CLS)&lt;/a&gt;&lt;/strong&gt;: measures visual stability. To provide a good user experience, pages should maintain a CLS of less than &lt;strong&gt;0.1&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The new list now looks like this:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7uCCbEOHmUIhmrhxHDuyrT/22f73ab59eedd00e6e4a64820d5b67de/image.png&quot; alt=&quot;New search signal metrics&quot;&gt;&lt;/p&gt;
&lt;h2&gt;When is it coming?&lt;/h2&gt;
&lt;p&gt;The metrics have already been released in May 2020. however, due to COVID-19, Google has decided to delay the rollout its Search ranking changes until next year.&lt;/p&gt;
&lt;p&gt;There was no specific date given for the rollout next year. It did promise to send out notice at least six months before the release to give companies enough time to take actions.&lt;/p&gt;
&lt;h2&gt;Why should businesses care?&lt;/h2&gt;
&lt;p&gt;Businesses should be preparing for this now because Google will be incorporating Core Web Vitals into their search algorithm some time next year (could be as early as Jan 2021). As mentioned already, these metrics will contribute to how Google judge if a site should be ranked higher or lower on their search results page. Falling in ranking number will lead to businesses going over to competitors, or worse disappear from the customer entirely if the site drops off the first Google results page. If this is sounding familiar, that&apos;s because this is another way of saying these metrics will impact SEO in the future. If you would like to learn more, &lt;a href=&quot;https://www.theleverageway.com/blog/how-far-down-the-search-engine-results-page-will-most-people-go/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;How far down the search engine results page will most people go&lt;/a&gt; is a good article explaining why.&lt;/p&gt;
&lt;p&gt;For large sites, it will take time to bring improvements to all the pages. It may even require some significant amount of rework to make it happen, and it is hard to say as every site is different. But given the consequences, it is probably best to play it safe and get started on the work as soon as possible. &lt;/p&gt;
&lt;p&gt;On a positive note, by following these metrics, it will provide users with a much more pleasant experience. It might even be possible that these changes will drive up traffic and increase conversion.&lt;/p&gt;
&lt;h2&gt;Why should developers care?&lt;/h2&gt;
&lt;p&gt;No doubt, these metrics will significantly impact how websites are designed and built once Google releases change that affects search rankings. As developers, we should be putting monitoring in place, auditing existing sites, coming up with plans to align with the metrics as soon as possible. The last thing we want to do is be forced to explain to our managers and their managers, why all of a sudden, the website ranking is tanking and the company is losing revenue.&lt;/p&gt;
&lt;p&gt;As developers, it is our job to create high-quality websites anyway. It is a perfect opportunity for tech teams to explain to the business why it is worth spending time and energy on these aspects of the site that doesn&apos;t directly contribute to generating more revenue. I have seen many situations in the past where performance improvements labelled as &quot;tech debts&quot; get deprioritised when compared with new features that on the surface offers &quot;more business value&quot;. Consider this as a rare chance for tech teams to speak up and have solid reasons behind your back.&lt;/p&gt;
&lt;p&gt;The last benefit is that Google is baking in ways to measure these metrics in all of its tools. It should be easy for developers to identify problems and correct them with all the free tools available. These tools are straightforward to use, even for people who usually doesn&apos;t associate themselves with the performance and metrics side of web development, this should be relatively easy to pick up. It could even be a rare chance that open doors to a new career path or at least understand more about web performance.&lt;/p&gt;
&lt;h2&gt;How to prepare for the update?&lt;/h2&gt;
&lt;p&gt;Google has already provided a &lt;a href=&quot;https://web.dev/vitals-tools/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;list of tools&lt;/a&gt; which can help you get started.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2hXvQBw8ST8RZKCjdCST3F/28742571e77937893739596041935b88/image.png&quot; alt=&quot;Google&amp;#x27;s Tools&quot;&gt;&lt;/p&gt;
&lt;p&gt;All of the above tools are useful for getting started with Core Web Vitals. I recommend taking a look at a free, open-source tool called Sitespeed. It offers a lot more monitoring and metrics out of the box than these tools. It also integrates well with some of Google&apos;s tools, such as Lighthouse and Google Page Speed Insight (GPSI). As a whole, it is a much more complete product to rely on to measure website performance. If you are interested in Sitespeed, check out this article I recently wrote &lt;a href=&quot;/easily-test-your-site-for-improvements-with-sitespeed-io&quot;&gt;Easily test your site for improvements with Sitespeed.io&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;It is hard to argue against the list of metrics Google put out as Core Web Vitals, by enforcing these metrics it will massively benefit users. I am a little sad that a single company is driving all these changes, not to mention a business that gets most of its revenue from ads ($134.81 billion in 2019).&lt;/p&gt;
&lt;h2&gt;Additional Reading&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://web.dev/vitals/#core-web-vitals&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://web.dev/vitals/#core-web-vitals&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://webmasters.googleblog.com/2020/05/evaluating-page-experience.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://webmasters.googleblog.com/2020/05/evaluating-page-experience.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Easily test your site for improvements with Sitespeed.io]]></title><description><![CDATA[<p>Too often we forget to measure how our websites are doing, that is especially true for smaller sites like this blog. Not that it has never crossed my mind, but it just felt too much hassle to do. Recently, I found about Sitespeed.io, and it has changed everything! It is a collection of open-source tools are makes monitoring website performance super easy. In this article, I'll walk through how we can utilise it in our workflow.</p>]]></description><link>https://blog.hao.dev/easily-test-your-site-for-improvements-with-sitespeed-io</link><guid isPermaLink="false">https://blog.hao.dev/easily-test-your-site-for-improvements-with-sitespeed-io</guid><pubDate>Fri, 05 Jun 2020 18:23:00 GMT</pubDate><content:encoded>&lt;p&gt;Too often we forget to measure how our websites are doing, that is especially true for smaller sites like this blog. Not that it has never crossed my mind, but it just felt too much hassle to do. Recently, I found about Sitespeed.io, and it has changed everything! It is a collection of open-source tools are makes monitoring website performance super easy. In this article, I&apos;ll walk through how we can utilise it in our workflow.&lt;/p&gt;&lt;h2&gt;Why do we care about performance metrics?&lt;/h2&gt;
&lt;p&gt;Think of the metrics as a way of quantifying a vague statement like &quot;a good website&quot;. Achieving high scores in all the metrics doesn&apos;t mean your website is perfect (since there are ways to cheat). Instead, it should be the minimum baseline that we aim for.&lt;/p&gt;
&lt;p&gt;It probably doesn&apos;t make sense to be concerned about performance metrics for all the websites. For example, if you have a personal project that you don&apos;t care about, then it is probably not worth spending time to improve the metrics. For a website that has more traffic or is trying to increase its user base, then these metrics must be taken seriously.&lt;/p&gt;
&lt;p&gt;As mentioned already, achieving high scores doesn&apos;t mean a website will lead to success. If your goal is to do well in terms of SEO ranking (how Google search ranks your page on their results page) or just simply offer a better user experience, then it would be silly to ignore these metrics.&lt;/p&gt;
&lt;h2&gt;Sitespeed&lt;/h2&gt;
&lt;p&gt;I ran Sitespeed against this site, and here are the results.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/v54eII41Tz897K7Bt6oKA/53d1925d6a8f9d9e0c4fd383c953c833/Screenshot_2020-06-04_at_11.33.08.png&quot; alt=&quot;Sitespeed metrics&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see, it gives you a lot of insights into what is happening behind the scenes. If this is still not enough, then see the section below on how to extend Sitespeed&apos;s functionality through plugins.&lt;/p&gt;
&lt;h2&gt;Collecting metrics&lt;/h2&gt;
&lt;p&gt;There are two ways to run Sitespeed on any page. It is possible to run it using Docker or just through NPM.&lt;/p&gt;
&lt;p&gt;Note: I prefer not locking to any particular versions since I just want to get a one-time measurement of the metrics. So the code examples below are all using the latest version each time you run it. However, if you do wish to monitor the metrics over a more extended period, then it might be best to stick with a particular version to avoid future breaking changes.&lt;/p&gt;
&lt;h3&gt;Collect metrics via Docker&lt;/h3&gt;
&lt;p&gt;Here is the command to run Sitespeed on this blog:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run --rm -v &quot;$(pwd)&quot;:/sitespeed.io sitespeedio/sitespeed.io:latest https://haodong.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Collect metrics via NPM package&lt;/h3&gt;
&lt;p&gt;If you prefer the NPM way, then it is even simpler.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npx sitespeed.io https://haodong.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Reading the results&lt;/h2&gt;
&lt;p&gt;After running Sitespeed it will create a folder of artefacts upon completion in the folder where you ran the command. In my case, the folder is called &lt;code class=&quot;language-text&quot;&gt;sitespeed-result&lt;/code&gt;. Then simply open &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; in the browser to see the results.&lt;/p&gt;
&lt;p&gt;The most useful page is probably the Summary page. If you wish to see more details around a particular metric, then the other pages will offer both more information and in some cases suggestions on how to improve.&lt;/p&gt;
&lt;p&gt;Note: It is possible to run Sitespeed on multiple pages, don&apos;t worry the folder &lt;code class=&quot;language-text&quot;&gt;sitespeed-result&lt;/code&gt; will not be overwritten. Instead, the results of each page will be inside the &lt;code class=&quot;language-text&quot;&gt;sitespeed-result/pages&lt;/code&gt; folder. It is also fine to run Sitespeed multiple times on the same page, results of each run is timestamped so nothing will be overwritten this way.&lt;/p&gt;
&lt;h2&gt;Sitespeed Plugins&lt;/h2&gt;
&lt;p&gt;It is even possible to add in plugins to get even more out of it. Here is an example of me collecting Lighthouse metrics.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5t0omZi118LyoUFIVA0RGm/986c930986e69a637734f480056037ca/Screenshot_2020-06-04_at_11.47.00.png&quot; alt=&quot;Lighthouse plugin&quot;&gt;&lt;/p&gt;
&lt;p&gt;The command to add Lighthouse plugin:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run --shm-size=1g --rm -v &quot;$(pwd)&quot;:/sitespeed.io sitespeedio/sitespeed.io:latest-plus1 https://haodong.io --plugins.add analysisstorer --plugins.add /lighthouse&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;However, I prefer to use the pre-built Docker container that includes both Lighthouse and the Google PageSpeed Insights plugin inside already.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run --shm-size=1g --rm -v &quot;$(pwd)&quot;:/sitespeed.io sitespeedio/sitespeed.io:latest-plus1 https://haodong.io&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: Documentation doesn&apos;t say how to run plugins with NPM, so I&apos;d recommend going with Docker instead.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;Hopefully, this article showcased how easy it is to get some very detailed metrics on your site. The only downside is that we need to run this command on our pages every-so-often to monitor it, which could be inconvenient for some.&lt;/p&gt;
&lt;p&gt;Luckily it is very straightforward to automate this with Netlify and Github webhooks. E.g. on merge to master, kick off a build. I will be posting a guide shortly, keep an eye on this space!&lt;/p&gt;
&lt;h2&gt;Related reading&lt;/h2&gt;
&lt;p&gt;I recently published another article &lt;a href=&quot;/are-you-ready-for-google-core-web-vitals&quot;&gt;Are you ready for Google&apos;s Core Web Vitals&lt;/a&gt;, it is a perfect use case for Sitespeed.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A non-technical introduction to git]]></title><description><![CDATA[<p>Git is often explained in a way that is too complex for the non-technical people to grasp its usefulness or the purpose of adopting it in the business. In my opinion, it is such a simple (yet powerful) concept that anyone should be able to understand easily with some simple explaining.</p>]]></description><link>https://blog.hao.dev/a-non-technical-introduction-to-git</link><guid isPermaLink="false">https://blog.hao.dev/a-non-technical-introduction-to-git</guid><pubDate>Thu, 04 Jun 2020 05:10:00 GMT</pubDate><content:encoded>&lt;p&gt;Git is often explained in a way that is too complex for the non-technical people to grasp its usefulness or the purpose of adopting it in the business. In my opinion, it is such a simple (yet powerful) concept that anyone should be able to understand easily with some simple explaining.&lt;/p&gt;&lt;p&gt;Git is one of the pillars of software development today. It enables software engineers to build software one step at a time, and in times of need, it offers engineers the most powerful tool in the world - the ability to restart from a last known good state.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Git is a free and open-source distributed version control system designed to handle everything from small to very large projects with speed and efficiency. - Git&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ok, so Git is a version control system for software development. But what is version control, and why is this useful?&lt;/p&gt;
&lt;h2&gt;Why is version control useful&lt;/h2&gt;
&lt;p&gt;To answer these questions, let&apos;s look at a very well established product that most people probably have heard of - Google Doc. When you are working on a Google Doc, did you ever notice this link on any non-empty Google Doc?&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7iyTlbEdBO1jAggYMih80k/ac4129c3254258afed83fda1a805ffce/screenshot-2020-05-25-at-20.54.41-1.png&quot; alt=&quot;screenshot-2020-05-25-at-20.54.41-1&quot;&gt;&lt;/p&gt;
&lt;p&gt;If we click on this link, then we are presented with all the edits that we have done (see screenshot below).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2rc4t1zHh7gAFaliLkHspn/6a169801e3b1ec7f2d4f7892dab7b1c5/screenshot-2020-05-25-at-20.57.32-1.png&quot; alt=&quot;screenshot-2020-05-25-at-20.57.32-1&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice the right-hand panel that says &quot;version history&quot;. Under it, it shows each edit we ever made in this document as a separate version. It is handy for a few different reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;All changes are recorded in a time series, from oldest to newest. With this feature, we can go back all the way to start of the document when it was blank. Think of it as the regular &quot;undo&quot; action on steroids.&lt;/li&gt;
&lt;li&gt;We can browse and replay changes like it was a movie, which is extremely useful when we would like to investigate something at a later point. e.g. for understanding why a paragraph changed to the way it is.&lt;/li&gt;
&lt;li&gt;It allows us to track who edited what and when. If there is an issue, then we will know who to walk to about it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Git is doing the same thing for software engineers. It provides all of the above benefits to software development and more.&lt;/p&gt;
&lt;h2&gt;More Reading&lt;/h2&gt;
&lt;p&gt;If you are interested, here&apos;s a &lt;a href=&quot;https://git-scm.com/book/en/v2/Getting-Started-A-Short-History-of-Git&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;brief history of Git&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Setting up Gatsby incremental build on Netlify for existing project]]></title><description><![CDATA[<p>Gatsby incremental build has been such a highly anticipated feature. Without it, using Gatsby in large projects is unwise due to the poor scaling that comes with the increasing amount of content. Even for this small blog site, I've been feeling the pain, every time i need to deploy a minor fix. The upgrade process of anything is never smooth, so here are the issues I encountered. Maybe this will help some of you out there!</p>]]></description><link>https://blog.hao.dev/setting-up-gatsby-incremental-build-on-netlify-for-existing-project</link><guid isPermaLink="false">https://blog.hao.dev/setting-up-gatsby-incremental-build-on-netlify-for-existing-project</guid><pubDate>Tue, 02 Jun 2020 18:28:00 GMT</pubDate><content:encoded>&lt;p&gt;Gatsby incremental build has been such a highly anticipated feature. Without it, using Gatsby in large projects is unwise due to the poor scaling that comes with the increasing amount of content. Even for this small blog site, I&apos;ve been feeling the pain, every time i need to deploy a minor fix. The upgrade process of anything is never smooth, so here are the issues I encountered. Maybe this will help some of you out there!&lt;/p&gt;&lt;h2&gt;Step 1 - Gatsby version bump&lt;/h2&gt;
&lt;p&gt;To take advantage of the new Gatsby feature, you need to be on v2.20.4 or higher. After checking the status of my repository with &lt;code class=&quot;language-text&quot;&gt;ncu&lt;/code&gt; (npm-check-updates), I decided to bring everything up-to-date.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/TWxlClyfDc6KWBHB1XhH3/40dcd828a9267e65c7a752a0cbbc2ae6/Screenshot_2020-06-02_at_20.00.05.png&quot; alt=&quot;dependencies out of date&quot;&gt;&lt;/p&gt;
&lt;p&gt;It took less than five lines to address breaking changes in the entire repository. &lt;code class=&quot;language-text&quot;&gt;react-helmet&lt;/code&gt; was the only repo&apos;s breaking change that affected the code.&lt;/p&gt;
&lt;h2&gt;Step 2 - Enable Netlify Build Plugins&lt;/h2&gt;
&lt;p&gt;The Netlify plugins are currently in beta, but from my experience, everything we are trying to do here is very stable. &lt;a href=&quot;https://app.netlify.com/enable-beta&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Click here&lt;/a&gt; to enable Netlify Build Plugins. &lt;/p&gt;
&lt;h2&gt;Step 3 - Add gatsby-cache-netlify-build Plugin&lt;/h2&gt;
&lt;p&gt;The new incremental build feature is based on Gatsby&apos;s new ability to cache &lt;code class=&quot;language-text&quot;&gt;public&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;.cache&lt;/code&gt; folders for future builds. Luckily, we only need to add two lines of text into Netlify config to take advantage of this.&lt;/p&gt;
&lt;p&gt;You could do this in two ways, one through the UI or use the Netlify config file. I recommend the config file because it is much simpler to do and more straightforward for me to explain.&lt;/p&gt;
&lt;p&gt;If you haven&apos;t done it already, add &lt;code class=&quot;language-text&quot;&gt;netlify.toml&lt;/code&gt; to the root of your repository. Then paste in the following snippet, and then we are done!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key property&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;netlify-plugin-gatsby-cache&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you are using other plugins, make sure you still paste in the &lt;code class=&quot;language-text&quot;&gt;[[plugins]]&lt;/code&gt;. See the example below for more information.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;toml&quot;&gt;&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key property&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;an-awesome-plugin&quot;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token key property&quot;&gt;package&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;netlify-plugin-gatsby-cache&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Step 4 - Use the new gatsby build command&lt;/h2&gt;
&lt;p&gt;Use the &lt;code class=&quot;language-text&quot;&gt;GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES&lt;/code&gt; flag to let Gatsby know to build incrementally. Change the default build command to:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt; # change this original command
&quot;build&quot;: &quot;gatsby build&quot;

# to
&quot;build&quot;: &quot;GATSBY_EXPERIMENTAL_PAGE_BUILD_ON_DATA_CHANGES=true gatsby build&quot;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Note: There is also an optional &lt;code class=&quot;language-text&quot;&gt;--log-pages&lt;/code&gt; flag for seeing extra logs.&lt;/p&gt;
&lt;h2&gt;Step 5 - Check Node version&lt;/h2&gt;
&lt;p&gt;One of the issues I experienced is that the new Gatsby version requires Node v10 LTS version. Building with Node v8.x.x will result in the following error on Netlify.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7zOZjUyBN48VSqMF90tmx7/69d953d8ba2f4dde736e9fc737a51fae/Screenshot_2020-06-02_at_19.28.14.png&quot; alt=&quot;Gatsby build failed&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Node.js version 8 reached End-of-life status on December 31, 2019. Many of Gatsby’s dependencies are updating to Node.js version 10 and above. Gatsby must also update in order to deliver new features and bug fixes more quickly.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Add a &lt;code class=&quot;language-text&quot;&gt;.nvmrc&lt;/code&gt; file into root of the repository and paste in the following to ensure we always use Node v10 (LTS) to build on Netlify.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yml&quot;&gt;&lt;pre class=&quot;language-yml&quot;&gt;&lt;code class=&quot;language-yml&quot;&gt;10&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;The end&lt;/h2&gt;
&lt;p&gt;That is it, push all your changes and see the incremental build on Netlify :)&lt;/p&gt;
&lt;h3&gt;Update&lt;/h3&gt;
&lt;p&gt;After updating all the dependencies and turning on incremental builds, I actually saw an increase in build time on average. This might be due to all the dependencies I updated at the same time, let me know in the comments if you experience the same problem. Or am I missing something here?&lt;/p&gt;
&lt;p&gt;Build time before
&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3qtqCC6vHnkJBQX3cKICJ3/95c4e43c90531989ba5974b8e9c5d7bd/Screenshot_2020-06-02_at_21.40.40.png&quot; alt=&quot;Build time before&quot;&gt;&lt;/p&gt;
&lt;p&gt;Build time after
&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/18tMSVsfpeMUXd2ah4tE26/e13834e8dad79767837b01d3488a9602/Screenshot_2020-06-02_at_21.47.02.png&quot; alt=&quot;Build time after&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Tips on how to start and finish personal projects]]></title><description><![CDATA[<p>2020 has been chaotic so far, I hope everything returns to normal soon and best wishes for everyone. On the bright side, now the UK government has recommended people to avoid going to mass gatherings and businesses have told their employees to work remote. It is a perfect time to hammer out some personal projects. I have always struggled to work on personal projects on the side over the years, however, this year I’ve managed to complete 2 projects in 2 months. I know this is a common problem, so I’d love to share my experiences in this post.</p>]]></description><link>https://blog.hao.dev/tips-on-how-to-start-and-finish-personal-projects</link><guid isPermaLink="false">https://blog.hao.dev/tips-on-how-to-start-and-finish-personal-projects</guid><pubDate>Tue, 17 Mar 2020 00:04:00 GMT</pubDate><content:encoded>&lt;p&gt;2020 has been chaotic so far, I hope everything returns to normal soon and best wishes for everyone. On the bright side, now the UK government has recommended people to avoid going to mass gatherings and businesses have told their employees to work remote. It is a perfect time to hammer out some personal projects. I have always struggled to work on personal projects on the side over the years, however, this year I’ve managed to complete 2 projects in 2 months. I know this is a common problem, so I’d love to share my experiences in this post.&lt;/p&gt;&lt;h2&gt;1. Find motivation&lt;/h2&gt;
&lt;p&gt;It is too tempting to watch something on TV or play games after a day&apos;s work. After all, these entertainments are distractions specially designed and engineered to hook us in. I find it genuinely difficult to master enough motivation to open up my laptop and code when it is so much easier to not be productive. From my own experience, only meaningful ideas will generate enough desire and motivation to work on it.&lt;/p&gt;
&lt;p&gt;So what is a meaningful project?&lt;/p&gt;
&lt;p&gt;I believe meaningful comes in two forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It is fun for you. Yep, even more fun than jumping into your favourite game or watch that TV show someone recommended to you.&lt;/li&gt;
&lt;li&gt;There are consequences if you don’t finish. E.g. someone is paying you for it, or it is an app you desperately need for yourself (I&apos;ll talk more about it later).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I&apos;ve tried to work on projects that I don&apos;t care much about, but started on them anyway because I felt other people might use it. Often than not, these projects are abandoned very quickly. Why? Because it is not meaningful enough for me. A promise of someone in the future may use it is all smoke and mirrors until it happens, and the unknown could drain away any motivation and interest when the project hits a blocker. These blockers could come in many forms, I won&apos;t go into many details here.&lt;/p&gt;
&lt;p&gt;In my opinion, it is much better to finish something that probably no one will ever use, compared to building a “sound” project but never end up finishing it.&lt;/p&gt;
&lt;h2&gt;2. Focus&lt;/h2&gt;
&lt;p&gt;If you are anything like me, then you also have the &quot;I have too many ideas&quot; problem. This could be switching to work on a new project halfway, or just increasing scope in the current project like changing frameworks and libraries. I am completely aware that I’m my demon when it comes to getting something done. So I’m always in search of productivity tools, hoping it could be the thing I&apos;m missing from my workflow and that it will get me to finish my side projects.&lt;/p&gt;
&lt;p&gt;However, the real problem often isn’t the underlying technology or tool. Instead, it’s the mindset of taking the side project serious enough to see it through. Mindset is such a blur, vague and BS sounding word that is overused in self-help contents. I hate to say it, but it did capture my problem. I tried to use scrum methodology in my projects, or kanban boards, but it didn’t matter what I used the result was no different. I have been reading the Shape Up book by Ryan Singer, which talks about how to build the right product and ship it too.&lt;/p&gt;
&lt;p&gt;My key takeaways from the book are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Work on a personal project in 6 weeks cycles. This way you could technically build 8 projects in a year with time to spare. In 6 weeks, there is enough time to work on a project that is not too simplistic, but not long enough to get bored with it.&lt;/li&gt;
&lt;li&gt;Must deliver the planned scope at the end of the 6 weeks. This assumes that you will do whatever it takes to finish the goal set. This means working late into the evenings or even whole weekends had to. It feels like a terrifying commitment, so make sure the scope is realistic and always go for less scope if unsure. After all, it is better to have something basic working than nothing at all.&lt;/li&gt;
&lt;li&gt;Use both words and sketches to define the scope and the solution. Don’t waste time building wireframes, or worse, pixel-perfect designs for personal projects. Remember designs don’t bring value on their own if the goal is to release something at the end of 6 weeks. Sketches should be enough to give a vague clarification which is enough at this stage. Don’t forget to describe in words the constraints and scope of this period.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;3. Discover Project Ideas&lt;/h2&gt;
&lt;p&gt;I have plenty of terrible and weird ideas, so I never really struggle with this. But many people I speak to don’t have anything they want to work on. So here are a few of my favourite questions which hopefully will help you to find some meaningful side project ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Is there anything that bothers you? That you just wished there was a simpler or better way to do it?&lt;/li&gt;
&lt;li&gt;Is there something you know people wanted but doesn’t exist? Who would be wanting to try it out on day one?&lt;/li&gt;
&lt;li&gt;What would be something cool to build which you’d be proud to show off to others?&lt;/li&gt;
&lt;li&gt;What could you create to make your personal or work life easier?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are lots of resources online which can probably help you to find the right project idea better than my questions here. It doesn’t matter how or where you find the inspiration, it doesn’t even matter if it is not original. One of the two projects I completed this year is a Todo app, there are already countless of Todo apps out there. But that doesn’t matter, I wanted a Todo app that I could adjust to how I see fit, whereas other solutions are less flexible. So the important thing is to find something meaningful to you.&lt;/p&gt;
&lt;h2&gt;4. Improve ideas by adding notes over time&lt;/h2&gt;
&lt;p&gt;Try to record ideas that come to you in a central place. This way you could always look up the ideas when the time comes like browsing a catalogue, instead of pulling out hairs trying to think of something on the spot.&lt;/p&gt;
&lt;p&gt;Another advantage of keeping everything documented is that even if you don&apos;t work on a project idea right now, it is always good to add in notes or thoughts over time. For example, I had lots of notes written up for the Todo app over time, ranging from sketches, features, milestones, etc.&lt;/p&gt;
&lt;h2&gt;5. Choose what to work on&lt;/h2&gt;
&lt;p&gt;Okay, let’s say you were able to generate many ideas and stored them in one place. Which one should you work on next? It is hard to give strict guidelines on how to pick the ideas, but I always remind myself to pick something I am motivated enough to work on for the next 6 weeks. It doesn’t matter if the idea is good or not, just make sure you have enough motivation to finish it.&lt;/p&gt;
&lt;p&gt;My final advice here is to always finish a product. Always try to finish a project, it is a good mental boost, gives confidence and increases motivation to tackle the next project. No matter how pointless the idea may seem, there are always value to be found in doing the work. First, it proves you were able to successfully build something, perhaps something to add to your work portfolio? Secondly, don’t forget that you’ve learned, practised and enhanced your skills in the process. Maybe next time when you need to implement something similar, you’ll not only have more experience but also a source of reference. Lastly, you get to tick something off your ideas list instead. The feeling of marking something as complete is awesome!&lt;/p&gt;
&lt;h2&gt;6. Set yourself up for success&lt;/h2&gt;
&lt;p&gt;Firstly, no matter what you do, try not to work on multiple projects at the same time. Stick to one thing and get it done, then work on the next idea. Context switching is expensive and more importantly, it confuses when you finally decide to do something productive.&lt;/p&gt;
&lt;p&gt;It is hard enough for me to find the motivation to open my laptop in the first place. It will just make the problem worse if I had to decide between multiple projects which one I want to tackle at the same time. The thought about justifying what to work on is enough to drain out my energy and turn me towards some kind of entertainment that requires less processing power e.g. watching TV.&lt;/p&gt;
&lt;p&gt;The goal here is to make working on side project the path of least resistance. Prepare your desk where you normally work, clean it up, put your laptop in the middle ready to rock. This way all you have to do when you come back from work is sit down and then you are off.&lt;/p&gt;
&lt;h2&gt;7. Reduce Friction &amp;#x26; Cost&lt;/h2&gt;
&lt;p&gt;When working on personal projects or side projects, you are likely a one-man-army. Where you wear multiple hats, Design, Developer, Quality Assurance and Product Manager. Try to make use of all the free (tier) tools out there, avoid reinventing the wheel where ever possible.&lt;/p&gt;
&lt;p&gt;There are lots good CI/CD tools out there, take your pick, just make sure if something could be automated then do it. Of course don&apos;t spend weeks on setting up the best pipeline ever, just set up something simple and usable.&lt;/p&gt;
&lt;p&gt;In recent years, there&apos;s a massive push for open source. Many tools and services will let people use their full product for free for open source projects. There are even more tools to pick from if consider paid services which gives a free tier. The free tier is often more than enough for side projects. &lt;/p&gt;
&lt;p&gt;Some other tips:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&apos;t host your apps, stick with static web apps when possible, avoid node apps since they require a server to run.&lt;/li&gt;
&lt;li&gt;Query database from client-side at the start, use something like &lt;a href=&quot;https://fauna.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;FaunaDB&lt;/a&gt; or &lt;a href=&quot;https://firebase.google.com/docs/firestore&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Firebase FireStore&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Use a headless CMS if there are lots of content.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Maybe some of these options don’t offer the best scaling, may lock you into a platform, or might have some security concerns. I’d argue it is best to worry about it later, have something working first will motivate you to come back and improve it. Whereas if you spend 4 weeks building a secure auth solution, it is highly probable that you’d give up, because let’s face it, that is not what most people find exciting. Of source, this doesn’t mean don’t build things properly, make sure things are well-rounded before a proper release.&lt;/p&gt;
&lt;p&gt;For more information on tools and services, check out a previous post on &lt;a href=&quot;https://haodong.io/awesome-tools-to-use-in-personal-projects&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Awesome tools to use in personal projects&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;My two projects&lt;/h2&gt;
&lt;p&gt;For both inspirations&apos; sake and also to prove I didn&apos;t just make up my two side projects in two months stats, here they are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://slash-dash.netlify.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Slash-Dash&lt;/a&gt;: a basic word-based RPG game built with React, xState and Bootstrap CSS.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://organise.netlify.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Organise&lt;/a&gt;: yet another todo app built with Svelte, xState, Firebase FireStore, Serina (one of my open-source projects), and some Bootstrap CSS. It also supports PWA.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Slash-Dash was meaningful to me since it was quite fun to build, I tried to recreate some aspects of a typical RPG game (e.g. kill monsters, get drops, change equipment, etc). I showed it off to a few people and it was nice to get feedback and boosted my motivation to do more work on it.&lt;/p&gt;
&lt;p&gt;Organise todo app is something I had been planning to create in years. This must be the 6th attempts to create a todo app. This project is meaningful because I wanted something more customised to solve my challenges. To make sure it is clever enough, I even went out of my way and created a natural language parser for time and dates, see &lt;a href=&quot;https://www.npmjs.com/package/serina&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Serina&lt;/a&gt; on NPM.&lt;/p&gt;
&lt;p&gt;So what did I do differently this time compared to the last 5 times?&lt;/p&gt;
&lt;p&gt;The first thing I did was to create something extremely basic very quickly. Then I fully switched to this app, even before I implemented edit and remove functionality. Now I&apos;ve locked myself into my app, if I wanted something, I&apos;d have to go and build it otherwise I just have to deal with the pain point again and again. Needless to say, it was super frustrating at the start when the app was unusable, but it motivated me to add the missing features and fix annoying bugs.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to configure user snippets in VS Code]]></title><description><![CDATA[<p>VS Code often has you covered when it comes it auto-suggestions and adding in snippets of code. But there will be times where something that is often used by you but isn't provided by VS Code, this is a great opportunity to reach out to the User Snippets feature in VS Code. Where you could add code snippets templates that make it easier to enter repeating code patterns, such as loops or conditional-statements. It is super easy to set up, but there are a few simple things to be aware of to get the best experience with it. We'll talk about these in this blog post.</p>]]></description><link>https://blog.hao.dev/how-to-configure-user-snippets-in-vs-code</link><guid isPermaLink="false">https://blog.hao.dev/how-to-configure-user-snippets-in-vs-code</guid><pubDate>Wed, 11 Mar 2020 22:45:00 GMT</pubDate><content:encoded>&lt;p&gt;VS Code often has you covered when it comes it auto-suggestions and adding in snippets of code. But there will be times where something that is often used by you but isn&apos;t provided by VS Code, this is a great opportunity to reach out to the User Snippets feature in VS Code. Where you could add code snippets templates that make it easier to enter repeating code patterns, such as loops or conditional-statements. It is super easy to set up, but there are a few simple things to be aware of to get the best experience with it. We&apos;ll talk about these in this blog post.&lt;/p&gt;&lt;h2&gt;Create Your Snippets&lt;/h2&gt;
&lt;p&gt;There is a section on this on the &lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets#_create-your-own-snippets&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&apos;s a snippet that I use all the time:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in file &apos;Code/User/snippets/typescript.json&apos;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token string-property property&quot;&gt;&quot;Unit Test Describe&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		    &lt;span class=&quot;token string-property property&quot;&gt;&quot;prefix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;desc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		    &lt;span class=&quot;token string-property property&quot;&gt;&quot;body&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
		        &lt;span class=&quot;token string&quot;&gt;&quot;describe(&apos;$1&apos;, () =&gt; {&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		        &lt;span class=&quot;token string&quot;&gt;&quot;\\t&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		        &lt;span class=&quot;token string&quot;&gt;&quot;});&quot;&lt;/span&gt;
		    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
		    &lt;span class=&quot;token string-property property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Write out describe block&quot;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The whole config is a JSON file, so make sure whatever the file remains as valid JSON for it to work correctly.&lt;/p&gt;
&lt;p&gt;For each snipper you could give it a name, in this case I called this snippet &quot;Unit Test Describe&quot;.&lt;/p&gt;
&lt;p&gt;Then inside the block of each snippet you could define 3 properties:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;prefix&lt;/code&gt; - keyword(s) you need to type to see it in the auto-suggest dropdown
&lt;code class=&quot;language-text&quot;&gt;body&lt;/code&gt; - what will be output once you select the snippet from auto-suggest dropdown
&lt;code class=&quot;language-text&quot;&gt;description&lt;/code&gt; - optional&lt;/p&gt;
&lt;p&gt;I picked &lt;code class=&quot;language-text&quot;&gt;desc&lt;/code&gt; for my snippet since it is easier than typing out description.&lt;/p&gt;
&lt;p&gt;As for body, each line in the array represents a separate line in the editor. Notice how I was even able to add in the correct indentation with &lt;code class=&quot;language-text&quot;&gt;\t&lt;/code&gt;. The other interesting thing is the use of &lt;code class=&quot;language-text&quot;&gt;$1&lt;/code&gt; , this is where the cursor will go after the snippet is added.&lt;/p&gt;
&lt;p&gt;So in summary, each time I type &lt;code class=&quot;language-text&quot;&gt;desc&lt;/code&gt; then selecting it in the auto-suggestions, I will get the following snippet added. the focus is automatically inserted in between the single quotes. This and a few other user snippets makes writing unit tests so much faster.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Improve Snippet Priority&lt;/h2&gt;
&lt;p&gt;Depending on what other VS Code plugins are installed, user snippet may not show up at the top of the suggestions as you code. If this is happening to you, then you could get user snippets to show up at the top of the list by adding the following settings option.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in file &apos;Code/User/settings.json&apos;&lt;/span&gt;

&lt;span class=&quot;token string-property property&quot;&gt;&quot;editor.snippetSuggestions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;top&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here&apos;s what the option looks like in the UI.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3Fn4tiegUeNJNpiDtAKtp3/9021a3bd0d7e5910fbd40ead07a82d74/Screenshot_2020-03-11_at_22.35.20.png&quot; alt=&quot;Snippet suggestion option in VS Code&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Snippet Scope&lt;/h2&gt;
&lt;p&gt;Again, this section is also covered by the &lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-scope&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;My suggestion here is to scope it to the language, as this will carry through different projects. If you use one project for each repository and works on many repositories at the same time, scoping to language makes much more sense.&lt;/p&gt;
&lt;h2&gt;Advanced Snippet Configs&lt;/h2&gt;
&lt;p&gt;I don&apos;t like using custom snippets heavily. I believe in
However, if you prefer to explore the possibilities of user snippets, have a look at the &lt;a href=&quot;https://code.visualstudio.com/docs/editor/userdefinedsnippets#_snippet-syntax&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Syntax&lt;/a&gt; section in the documentation. It supports placeholders, choice, variables, transforms, binding with shortcuts, etc.&lt;/p&gt;
&lt;h2&gt;Sharing Snippets&lt;/h2&gt;
&lt;p&gt;Sometimes team members find it useful to share snippets with each other. &lt;a href=&quot;https://code.visualstudio.com/api/language-extensions/snippet-guide#using-textmate-snippets&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Textmate&lt;/a&gt; is the tool for that!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why I would avoid Sequelize in the future]]></title><description><![CDATA[<p>Following on from a previous post on <a href="https://haodong.io/database-migration-knex-vs-typeorm-vs-sequelize" target="_blank" rel="nofollow noopener noreferrer">Database Migration: Knex vs TypeORM vs Sequelize</a>, I’d like to share my experience regarding Sequelize after using it in a commercial project. TLDR, it wasn't as good as I hoped and for the love of God don’t use it with Typescript! Read on to see why.</p>]]></description><link>https://blog.hao.dev/why-i-would-avoid-sequelize-in-the-future</link><guid isPermaLink="false">https://blog.hao.dev/why-i-would-avoid-sequelize-in-the-future</guid><pubDate>Sun, 08 Mar 2020 13:14:00 GMT</pubDate><content:encoded>&lt;p&gt;Following on from a previous post on &lt;a href=&quot;https://haodong.io/database-migration-knex-vs-typeorm-vs-sequelize&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Database Migration: Knex vs TypeORM vs Sequelize&lt;/a&gt;, I’d like to share my experience regarding Sequelize after using it in a commercial project. TLDR, it wasn&apos;t as good as I hoped and for the love of God don’t use it with Typescript! Read on to see why.&lt;/p&gt;&lt;h2&gt;How work was carried out&lt;/h2&gt;
&lt;p&gt;Due to how our project work was broke down and prioritised, I ended up writing the migration scripts first and integrated with codebase later.&lt;/p&gt;
&lt;p&gt;Here&apos;s the order of tasks:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Database table &amp;#x26; data migration&lt;/li&gt;
&lt;li&gt;Integrated with CI/CD pipeline&lt;/li&gt;
&lt;li&gt;Creating data models&lt;/li&gt;
&lt;li&gt;Linking up codebase to DB through Sequelize&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In hindsight, if it might have worked better if we completed Database table &amp;#x26; data migration last. Not that it would have made much difference in terms of implementation approach, but for sure it would have highlighted some issues much earlier on.&lt;/p&gt;
&lt;h2&gt;Writing migration scripts&lt;/h2&gt;
&lt;p&gt;When I wrote the initial migration scripts it was straight forward. The documentation wasn’t perfect, but with some light Googling, I was able to get it done. At this stage I haven’t spotted anything off-putting about Sequelize yet.&lt;/p&gt;
&lt;p&gt;Side note: It might be good to clarify how to add static data (record with fixed id values) into the database. It could either be done as &lt;strong&gt;DB migration&lt;/strong&gt; or &lt;strong&gt;DB data seed&lt;/strong&gt;. Just want to mention it here as we debated the two approaches. Just FYI, we have gone with the &lt;strong&gt;DB migration&lt;/strong&gt; approach at the end.&lt;/p&gt;
&lt;h2&gt;Migration pipeline&lt;/h2&gt;
&lt;p&gt;This part is not too relevant to the title of the blog, feel free to skip to the next section.&lt;/p&gt;
&lt;p&gt;The initial idea is to run the DB migration scripts for each commit pushed to master. This way we can version control our DB changes which is the main reason for using a DB migration tool. Therefore we could have a simple &lt;strong&gt;rollback&lt;/strong&gt; strategy in case we find a problem after a deployment. The project&apos;s pipeline is fairly complex, this is partly due to infrastructure is in the process of being migrated to a more modern setup.&lt;/p&gt;
&lt;p&gt;Although Sequelize provides a rollback solution natively, we still experienced problems with how to actually trigger it. The environment setup is super strict and for good reasons e.g. security. However, this means developers can&apos;t easily access the Database (not even on QA environment), so don&apos;t even ask me about Production.&lt;/p&gt;
&lt;p&gt;Given these strict environment configurations, how coule we rollback migration changes?&lt;/p&gt;
&lt;p&gt;We had a few ideas as a team:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ideal case: the CI pipeline would do Database migration first when something is pushed to master. We&apos;d also have a final step of the CI to run rollback if the build isn&apos;t successful. Unfortunately, making such a change wouldn&apos;t be easy or straightforward as this will require a lot of CI rework. Due to the fact no such setup has been done previously and the pipeline inherits from shared Jenkins scripts.&lt;/li&gt;
&lt;li&gt;Running rollback manually: because of strict envrionment setup, it wouldn&apos;t be easy to convince people to give the whole team access to Production. Personally, I think this is still useful to have in case of emergencies, even if there was a way to do auto rollback in CI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, we are still trying to find a nice solution for this. Maybe I&apos;ll post an update if we find a good solution.&lt;/p&gt;
&lt;h2&gt;Creating Models&lt;/h2&gt;
&lt;p&gt;This is the first time I started using Sequelize with Typescript, and this is where the pain started.&lt;/p&gt;
&lt;h3&gt;Lack of Typescript related documentation&lt;/h3&gt;
&lt;p&gt;Right at the start, I was hit with an issue. The Sequelize documentation for Typescript is lacking to say the least. Here is the &lt;a href=&quot;https://sequelize.org/master/manual/typescript.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt; and see for yourself. There is barely enough content to fit on 1 page of A4.&lt;/p&gt;
&lt;h3&gt;Unclean approach&lt;/h3&gt;
&lt;p&gt;Even if I look past the lack of documentation issue, there are other problesm.&lt;/p&gt;
&lt;p&gt;If you read the example provided on the doc, the way the models are declared and used isn&apos;t very clean in my opinion. The example shows that, to initialise a model Sequelize needs to establish a connection to the database and then passed into each model.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sequelize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sequelize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mysql://root:asd123@localhost:3306/mydb&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Model&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; ownerId&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; createdAt&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; updatedAt&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

Project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DataTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UNSIGNED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    autoIncrement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    primaryKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ownerId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DataTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UNSIGNED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    allowNull&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataTypes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;STRING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    allowNull&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  sequelize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  tableName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;projects&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice how sequelize is passed to the &lt;code class=&quot;language-text&quot;&gt;init()&lt;/code&gt; function after a connection has been established. The model itself is a Typescript interface, table column types and everything else is defined during the &lt;code class=&quot;language-text&quot;&gt;init()&lt;/code&gt; function, what?!&lt;/p&gt;
&lt;p&gt;When I was working on this, I really struggled to see how this was a good approach to both declaring models and using them like this. The fact there is a dependency between model initilisation and sequelize having established a DB connect feels odd.&lt;/p&gt;
&lt;p&gt;After I have spent more time with it, I can see how I could work around this issue. I also want to clarify, there isn&apos;t as much of a problem with the tool, instead, I feel like the documentation itself could be better. For example, if the example read something like the follow it would have made a lot more sense for first timers like me.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// server.ts&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; Project&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; projectModelConfig &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./models/project.model.ts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; sequelize &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sequelize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mysql://root:asd123@localhost:3306/mydb&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Project&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;init&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;projectModelConfig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  sequelize&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  tableName&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;projects&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// project.model.ts&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; projectModelConfig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DataTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UNSIGNED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    autoIncrement&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    primaryKey&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ownerId&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; DataTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;INTEGER&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;UNSIGNED&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    allowNull&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DataTypes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;STRING&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    allowNull&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Project&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Model&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; id&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; ownerId&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; name&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; createdAt&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; updatedAt&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; Date&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In the end, I decided to use &lt;code class=&quot;language-text&quot;&gt;sequelize-typescript&lt;/code&gt; library instead, mainly because their syntax seemed a lot cleaner and more maintainable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// server.ts
import Project, { projectModelConfig } from &apos;./models/project.model.ts&apos;;

const sequelize = new Sequelize({
    ...
    models: [__dirname + &apos;/**/*.model.ts&apos;]
});

// project.model.ts

import { Table, Column, Model } from &apos;sequelize-typescript&apos;;
 
@Table
class Project extends Model&amp;lt;Project&gt; {
 
  @Column({
    type: DataTypes.INTEGER.UNSIGNED,
    autoIncrement: true,
    primaryKey: true,
  })
	id: number;

  @ForeignKey(() =&gt; User)
  @Column
  ownerId: number;

	@Column({
		type: new DataTypes.STRING(128),
    allowNull: false,
	})
  name: string;
}&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Issue 3 - Decorators&lt;/h3&gt;
&lt;p&gt;By choosing &lt;code class=&quot;language-text&quot;&gt;sequelize-typescript&lt;/code&gt; I created another problem for myself. It uses decorators by default as you can see from the example above, which means I also had to turn on experimental decorators feature in Typescript. It wasn&apos;t until later I found out that it caused other issues when we tried to write unit tests.&lt;/p&gt;
&lt;h2&gt;Linking up Sequelize to Codebase&lt;/h2&gt;
&lt;h4&gt;Code&lt;/h4&gt;
&lt;p&gt;This process was painful, partly because we have a strict typing configuration in our project, but the fact Sequelize and Typescript doesn&apos;t work well together is definately a contributing factor as well.&lt;/p&gt;
&lt;p&gt;I&apos;ll just come out and say it Sequelize with Typescript is just confusing, even after adopting &lt;code class=&quot;language-text&quot;&gt;sequelize-typescript&lt;/code&gt; this statment still holds true. I encountered several problems caused by Typescript types, but I&apos;ll just focus on the most memorable one here.&lt;/p&gt;
&lt;p&gt;I wanted to inject Sequelize into a function to make testing a bit easier. But I simply couldn&apos;t get the type to work, I was really confused. After trial and error for ages, here is what I found out.&lt;/p&gt;
&lt;p&gt;Have a look at the code example below. The fact Sequelize (the one we import) is not Sequelize type blows my mind. But an instance of Sequelize (&lt;code class=&quot;language-text&quot;&gt;connectedSequelize&lt;/code&gt; in the example) can be assigned the Sequelize type. Again, what?!&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;import { Sequelize } from &apos;sequelize-typescript&apos;;

const connectedSequelize = new Sequelize(...);

// &quot;Sequelize&quot; is NOT Sequelize type
// &quot;connectedSequelize&quot; is Sequelize type&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So basically, I can&apos;t give Sequelize (the function param) a type because there is no type for it unless we create an instance of it. In the end, I had to leave the type to &lt;code class=&quot;language-text&quot;&gt;any&lt;/code&gt; and I feel like a horrible human being for doing it.&lt;/p&gt;
&lt;h4&gt;Unit Testing&lt;/h4&gt;
&lt;p&gt;There is litterally no documentation around how to do this properly nor are there good example shared online.&lt;/p&gt;
&lt;p&gt;Unit testing, in general, is messy for Sequelize. Paired it with Typescript is almost asking for trouble. I won&apos;t bore you with all the problems I experienced in too much depth, so here are some key ones I experienced:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are Decorators errors when trying to create a new instance of a Sequelize model in test. I ended up mocking all the models in functions that require them.&lt;/li&gt;
&lt;li&gt;Get error complaining the model isn&apos;t initialised by Sequelize. So I added the models without any test, probably something for me to revisit now I have some ideas.&lt;/li&gt;
&lt;li&gt;Sequelize query returns a list of data in its own prototype, so the type doesn&apos;t match with what you&apos;d expect the results to be. I had to resolve to any type in many instances when trying to mock something.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As you can see I didn&apos;t have a great deal of fun testing Sequelize code, especially considering this is with dependency injection (just imagine without). Normally, this would make the unit test extremely easy to write, that is not the case here.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;We picked Sequelize as our Database Migration tool for the same reasons highlighted in the post (see first paragragh). At the time, it seemed more mature than TypeORM and more modern than Knex. Unfortunately, as I found out the hard way that it isn’t modern enough.&lt;/p&gt;
&lt;p&gt;If you plan to use Sequelize in a pure JavaScript project, it will probably serve you fine. However, if there is a chance to be adding Typescript into your backend stack, then I’d recommend trying out TypeORM instead.&lt;/p&gt;
&lt;h2&gt;Other Thoughts&lt;/h2&gt;
&lt;p&gt;When I was talking to our DevOps team, I was introduced to the concept of Event Sourcing. Now looking back, maybe we could have approached our data differently and then there might not be a need for relational data structure. Thus avoiding using database migration tools altogether.&lt;/p&gt;
&lt;p&gt;Here is some top Google suggested reading materials on Event Sourcing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://microservices.io/patterns/data/event-sourcing.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://microservices.io/patterns/data/event-sourcing.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://docs.microsoft.com/en-us/azure/architecture/patterns/event-sourcing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://eventuate.io/whyeventsourcing.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://eventuate.io/whyeventsourcing.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://martinfowler.com/eaaDev/EventSourcing.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://martinfowler.com/eaaDev/EventSourcing.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Configuring custom CDN caching with AWS S3 and CloudFront]]></title><description><![CDATA[<p>I recently encountered a problem where we wanted to move some assets to AWS S3 and have AWS CloudFront CDN in front of it. The great benefit of having a CDN is to reduce cost and improve performance. The get the most out of this configuration we want to have the following set up:</p>
<p>Client (Browser) → (no cache) → CloudFront → (cache) → S3</p>]]></description><link>https://blog.hao.dev/configuring-custom-cdn-caching-with-aws-s3-and-cloudfront</link><guid isPermaLink="false">https://blog.hao.dev/configuring-custom-cdn-caching-with-aws-s3-and-cloudfront</guid><pubDate>Wed, 29 Jan 2020 08:29:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently encountered a problem where we wanted to move some assets to AWS S3 and have AWS CloudFront CDN in front of it. The great benefit of having a CDN is to reduce cost and improve performance. The get the most out of this configuration we want to have the following set up:&lt;/p&gt;
&lt;p&gt;Client (Browser) → (no cache) → CloudFront → (cache) → S3&lt;/p&gt;&lt;p&gt;Note that this set up might not be right for everyone, it makes sense for us because:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Normally, it is common to use hashed file names to indicate to browsers that they now need to use the new file instead of loading it from browser cache. But we want to avoid this approach since it would require us releasing many apps that consumes this assets.&lt;/li&gt;
&lt;li&gt;The benefit this brings is that we can control when to invoke cache (e.g. when we release a new version of the asset), as suppose to users caching files on their browser (e.g. if we release a new version since the browser believes it has the right version it won&apos;t try to download the latest version.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Key technology&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;S3 - is used for storing the files (AWS call them as &lt;strong&gt;Objects&lt;/strong&gt;) in folders (AWS call them &lt;strong&gt;Buckets&lt;/strong&gt;).&lt;/li&gt;
&lt;li&gt;CloudFront - a CDN gateway (AWS calls it a &lt;strong&gt;Distribution&lt;/strong&gt;) that we set up to just deal with caching, which would utilise all the different server farms Amazon has in different parts of the world. This is also a way to defend against DDOS attacks, since the requests never makes it past the CDN layer. It is also cheaper for this exact reason because there is no computation when requests are served by cache. Finally, this is also an opportunity to utilise AWS’s &lt;strong&gt;Lambda@Edge&lt;/strong&gt; to implement logic on CDN level.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Creating a S3 bucket&lt;/h2&gt;
&lt;p&gt;First we created a standard S3 bucket with mostly default settings. This is relatively easy to do, and AWS already has a decent &lt;a href=&quot;https://docs.aws.amazon.com/AmazonS3/latest/gsg/CreatingABucket.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;guide&lt;/a&gt; on how to do this.&lt;/p&gt;
&lt;h2&gt;Creating CloudFront Distribution&lt;/h2&gt;
&lt;p&gt;Then we set up a CloudFront Distribution with these changes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure that it is not possible to access Bucket files directly via URL, and to grant the Distribution access to the Bucket (so it can cache and serve it). Identify could be new or Existing depending what makes more sense.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4mv2xHH1R2UjB4NFVuOy90/fa677f1e30ac97cea2f353deb8e8ea8f/99adabb3-ac51-479d-a6e4-801a1ecbb092.png&quot; alt=&quot;CloudFront Set up Bucket Access&quot;&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Then change some of protocol to make sure we always to try serve with HTTPS. For Object Caching we select &lt;code class=&quot;language-text&quot;&gt;cusomize&lt;/code&gt; and give the TTL appropriate values.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4CSECQRQgptvwRCNoW87WS/7296e37e3be23a0540da15970318abcb/2fe5f4b7-9b44-487d-904b-0f610f3a80f3.png&quot; alt=&quot;CloudFront Behavior Policies&quot;&gt;&lt;/p&gt;
&lt;p&gt;For more information about the full set up, here is an official AWS YouTube video explaining in detail how to create a new distribution: &lt;a href=&quot;https://www.youtube.com/watch?v=KIltfPRpTi4&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.youtube.com/watch?v=KIltfPRpTi4&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Testing Cache is working correctly&lt;/h2&gt;
&lt;p&gt;This &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Expiration.html#ExpirationDownloadDist&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;AWS documentation&lt;/a&gt; suggest that it is best to use TTL option in CloudFront Distribution (see above screenshot) to control the cache here: AWS CloudFront → (cache) → AWS S3. Then use HTTP headers to disable cache between: Client → (no cache) → CloudFront.&lt;/p&gt;
&lt;p&gt;Here’s the quote taken from the link above:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;For web distributions, you can use Cache-Control or Expires headers, and CloudFront minimum, maximum, and default TTL values to control the amount of time in seconds that CloudFront keeps an object in the cache before forwarding another request to the origin. Headers values also determine how long a browser keeps an object in the cache before forwarding another request to CloudFront.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here explains how the headers and TTL works together. The thing to note here is that we need to use the &lt;code class=&quot;language-text&quot;&gt;minimum TTL&lt;/code&gt; in conjunction with &lt;code class=&quot;language-text&quot;&gt;Cache-Control: no-cache&lt;/code&gt; header.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4dFzQRwETtiwdSVHTaVcam/0a7d4800bdd83a66021076c4be6bd941/450664c1-057b-44ae-8ae3-c8cafdef7574.png&quot; alt=&quot;CloudFront Cache Table&quot;&gt;&lt;/p&gt;
&lt;p&gt;With the above understanding, then we tested our assumptions with the following test:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Upload a &lt;strong&gt;file.js (v1)&lt;/strong&gt; to S3 bucket and then set the minimum TTL on the Distribution to 1800 seconds (30 mins). &lt;/li&gt;
&lt;li&gt;Then try to request the file via the browser (goes to CloudFront) which returned &lt;strong&gt;file.js (v1)&lt;/strong&gt; as expected.&lt;/li&gt;
&lt;li&gt;We then uploaded a &lt;strong&gt;file.js (v2)&lt;/strong&gt; to the same S3 bucket which replaced &lt;strong&gt;file.js (v1)&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We tried to request file via the browser and got back &lt;strong&gt;file.js (v1)&lt;/strong&gt; still, this means the minimum TTL is working. So this part is correct: AWS CloudFront → (cache) → AWS S3.&lt;/li&gt;
&lt;li&gt;We then busted the cache on the Distribution.&lt;/li&gt;
&lt;li&gt;We request the file again via the browser and got back &lt;strong&gt;file.js (v2)&lt;/strong&gt;, this again proves that CloudFront is the one providing the cache and cache isn’t coming from the browser.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Here&apos;s a simpler version in the form of a sketch to illustrate the above:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1i29uyq8BtqRJoqHvXeHoz/6294ae13bd276e5ba550da05212a39ae/01498160-6d52-4979-b23c-6b96854aa199.jpeg&quot; alt=&quot;CloudFront Cache sketch&quot;&gt;&lt;/p&gt;
&lt;p&gt;The browser header is set to no-cache as well in this case, so we can be sure that we are just testing the CloudFront.&lt;/p&gt;
&lt;h2&gt;Invalidate Cache&lt;/h2&gt;
&lt;p&gt;As mentioned above, we want to be able to bust the cache after we re-release the asset. It turns out this is quite easy to do with the AWS console.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/eOCF2B9A64ckjRZizf07d/b50e037b7a9c58a21a16b38b2e6f44b8/Screenshot_2020-01-28_at_19.12.42.png&quot; alt=&quot;CloudFront Invalidate cache&quot;&gt;&lt;/p&gt;
&lt;p&gt;It is also possible to do it through AWS CLI, which we can use to invoke cache as part of the CI pipeline after we released a new version (see &lt;a href=&quot;https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/Invalidation.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official doc&lt;/a&gt;).&lt;/p&gt;
&lt;h2&gt;Custom domain name&lt;/h2&gt;
&lt;p&gt;If you don&apos;t like using the default CloudFront url (e.g. &lt;a href=&quot;https://wiebfpweufpwubfe.cloudfront.net&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://wiebfpweufpwubfe.cloudfront.net&lt;/a&gt;), it is very simple to update the CloudFront Distribution to use a custom domain.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1lR1yRqwdx7Jqz5vTCjztB/29263b5ef298a00e7685860a302db93a/2235cea4-32ad-40c9-8571-fb4623c47e74.png&quot; alt=&quot;CloudFront edit custom domain&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Database Migration: Knex vs TypeORM vs Sequelize]]></title><description><![CDATA[<p>I recently joined a full-stack team, and we decided to shift a prototype app into something that scales better in the future. There were a few decisions made about how we might achieve this goal, one of which is to introduce database migration to our CI/CD pipeline. This blog post will cover the key findings and the final implementation around it.</p>
<p>It is worth noting that at this point, the decision has already been made to go with AWS's Aurora relational database. This is due to there being a lot of relations between data so picking a relational database made sense, and picked Aurora to reduce the amount of maintenance. Although it is possible to hack around and use a document based database instead, but it just means more data duplication and introduces more maintenance headaches.</p>]]></description><link>https://blog.hao.dev/database-migration-knex-vs-typeorm-vs-sequelize</link><guid isPermaLink="false">https://blog.hao.dev/database-migration-knex-vs-typeorm-vs-sequelize</guid><pubDate>Tue, 28 Jan 2020 18:51:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently joined a full-stack team, and we decided to shift a prototype app into something that scales better in the future. There were a few decisions made about how we might achieve this goal, one of which is to introduce database migration to our CI/CD pipeline. This blog post will cover the key findings and the final implementation around it.&lt;/p&gt;
&lt;p&gt;It is worth noting that at this point, the decision has already been made to go with AWS&apos;s Aurora relational database. This is due to there being a lot of relations between data so picking a relational database made sense, and picked Aurora to reduce the amount of maintenance. Although it is possible to hack around and use a document based database instead, but it just means more data duplication and introduces more maintenance headaches.&lt;/p&gt;&lt;h2&gt;What is Database Migration?&lt;/h2&gt;
&lt;p&gt;Database schema changes is typically a concern in relational DBMS (Database Management Systems), document based DBMS doesn’t have this problem. Why? Relational DBMS often rely on established schema to enforce the relationships between records, so it is crucial to maintain these relationships. Document based DBMS often has no schema (at least not enforced by the DBMS itself), so as developers we could store anything we like in any records e.g. I could have a record storing user data, and the next record could be about different types of potatoes.&lt;/p&gt;
&lt;p&gt;Making changes to a database is probably unavoidable, sooner or later we will find ourselves in situations where we need to make some modifications. If we add a new field, then what should the default value be? If no default value is provided then the old records will have no data for that field, the application assuming the data might struggle to coup with this edge case since the app might always expect some data to be returned. Or worse, we might need to add or delete tables to a production database. What is the best way to make these changes, what should we do if something goes wrong?&lt;/p&gt;
&lt;p&gt;These questions are also solved by a database migration tool. A database migration tool should be able to iteratively introduce changes by based on its versioning system e.g. it might decide to do nothing if it is already on the latest version, but if it find it self behind in version it could then run the versions that’s missing and introduce those changes to the relevant database.&lt;/p&gt;
&lt;h2&gt;Investigation&lt;/h2&gt;
&lt;p&gt;Having decided we wanted to implement DB migrations and settled on the Database schema as a team, the next step is  to investigate what might be the best tool for database migrations. Our API is using Node, so the solution we are looking for here is a JavaScript based tool.&lt;/p&gt;
&lt;h3&gt;Expectations&lt;/h3&gt;
&lt;p&gt;In terms of expectations of this spike, we hoped to find something that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support adding, editing and deleting tables and column&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g. we might want to add new tables in the future or modify fields in a given table&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support inserting, editing and deleting records&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g. similarly, we have seeded data that we might want to modify later on&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support seeding data&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g. put add in static data such as brands, cookie categories, cookies and etc&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support versioned incremental migration changes (automatically)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g. we should be able to add new changes into a version controlled file, this should then be picked up and only new changes should be picked up and ran by the migration tool&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Support migration reverts (in the event of causing issues on production)&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;e.g. the tool should be able to take revert migration logic, which means we can safely and easily undo our migration if the changes causes issue on production&lt;/p&gt;
&lt;h3&gt;Different choices&lt;/h3&gt;
&lt;p&gt;After doing a bit of research online, these seems to be the top choices right now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;http://knexjs.org/#Installation-migrations&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Knex&lt;/a&gt; - already using this in our app&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/typeorm/typeorm/blob/master/docs/migrations.md&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TypeORM&lt;/a&gt; - is built for TypeScript and may end up being a better choice than Knex&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sequelize.org/master/manual/migrations.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sequelize&lt;/a&gt; seems to be very popular as well&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is worth noting that all of the above supports TypeScript according to their documentation pages.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3xUtcbeD509A0K65hiRrub/98788a6daa5fcac4ce518c43b7a2c166/Screenshot_2020-01-28_at_19.22.40.png&quot; alt=&quot;Knex v.s. TypeORM v.s. Sequelize NPM Trends updated&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Knex v.s. TypeORM v.s. Sequelize&lt;/h2&gt;
&lt;h3&gt;Expectations&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/44a5R8Lz3WBv9N4woSwCiu/2afc8648b29c9fc419fb2f1ec611a29d/Screenshot_2020-01-28_at_18.48.14.png&quot; alt=&quot;Knex vs TypeORM vs Sequelize Expectations table&quot;&gt;&lt;/p&gt;
&lt;p&gt;For TypeORM, there’s this nice unofficial plugin we can use &lt;a href=&quot;https://www.npmjs.com/package/typeorm-seeding&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TypeORM Seeding&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Codebase&lt;/h3&gt;
&lt;p&gt;All three libraries are actively maintained, with frequent commits with the last 30 days and before that.&lt;/p&gt;
&lt;p&gt;Here’s a breakdown of the numbers of issues and PRs on each repo:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3uQRjvLrQ2l18lkRtrtQD5/e1cd96daa9b4662f4aa6acb93530e43b/Screenshot_2020-01-28_at_18.49.24.png&quot; alt=&quot;Knex v.s. TypeORM v.s. Sequelize Github Issues and PRs&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Documentation&lt;/h3&gt;
&lt;p&gt;I went through the documentation all 3 libraries. Knex’s documentation is detailed but very difficult to read, both in terms of how the content is structured and presented. I found that everything I needed are documented, just not in an ideal way. TypeORM is on the other side of the scale, it is very well presented and has plenty of examples. I was able to find what I needed very quickly, and everything seems intuitive enough. However, there seems to be a lack of explanation for some of the sections. Often the documentation involves a brief statement, then shows an example and that’s it. Finally, Sequelize is somewhere in between. It seems to have the best documentation in terms of detail, its presentation isn’t as good as TypeORM but definitely better than Knex.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2iDMF8ILo15SlwMgZzctk9/9cc323f0d96816ad21b94d8044fc10a1/Screenshot_2020-01-28_at_18.49.30.png&quot; alt=&quot;Knex v.s. TypeORM v.s. Sequelize Documentation comparison&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Testability&lt;/h3&gt;
&lt;p&gt;All three options has similar levels of testability. It is possible to extract out migration logic to functions and then unit testing those functions. The only exception is that due to TypeORM doesn’t have official seeding functionality, unit testing it might be slightly complicated.&lt;/p&gt;
&lt;h3&gt;Typescript Support&lt;/h3&gt;
&lt;p&gt;On paper all three tools support Typescript.&lt;/p&gt;
&lt;p&gt;Knex admits that Typescript isn’t first class citizen.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;However it is to be noted that TypeScript support is currently best-effort. Knex has a very flexible API and not all usage patterns can be type-checked and in most such cases we err on the side of flexibility. In particular, lack of type errors doesn&apos;t currently guarantee that the generated queries will be correct and therefore writing tests for them is recommended even if you are using TypeScript.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Sequelize also mentions that Typescript probably will do very little when they are paired together.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;As Sequelize heavily relies on runtime property assignments, TypeScript won&apos;t be very useful out of the box. A decent amount of manual type declarations are needed to make models workable.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;TypeORM just mentions the fact they support Typescript. After some deeper digging around, I found the &lt;a href=&quot;https://github.com/typeorm/typescript-example&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TypeORM Typescript example&lt;/a&gt;. I could really find cases where TypeORM really made use of Typescript, see the code snippets below:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;ts&quot;&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// typescript-example/src/index.ts&lt;/span&gt;
  
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;createConnection&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;typeorm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Post&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./entity/Post&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Category&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;./entity/Category&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// connection settings are in the &quot;ormconfig.json&quot; file&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;createConnection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;then&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; connection &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; category1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    category1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;TypeScript&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;category1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; category2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    category2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Programming&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;category2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; post &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Control flow based type analysis&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;text &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;TypeScript 2.0 implements a control flow-based type analysis for local variables and parameters.&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    post&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;categories &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;category1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; category2&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; connection&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;manager&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;post&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Post has been saved: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; post&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;error &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;console&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Error: &quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; error&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// typescript-example/src/entity/Category.ts&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;Column&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; PrimaryGeneratedColumn&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Entity&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;typeorm&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Entity&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Category&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;PrimaryGeneratedColumn&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    id&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;number&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Column&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    name&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Suggestion&lt;/h2&gt;
&lt;p&gt;Best to avoid Knex since it seems to be the worse choice in many areas, there’s very little that it outshines either TypeORM or Sequelize.&lt;/p&gt;
&lt;p&gt;Out of TypeORM and Sequelize, Sequelize seems like the safer option with the added benefit of a bigger community. This means nice plugin and tools such as (&lt;a href=&quot;https://github.com/tomjschuster/sequelize-ui&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;sequelize-ui&lt;/a&gt;, &lt;a href=&quot;https://github.com/andyforever/sequelizer&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;sequelizer&lt;/a&gt;, etc).&lt;/p&gt;
&lt;p&gt;Last but not least, TypeORM looks like to be the new kid of the block making use of decorators and etc. Compared to Sequelize’s philosophy of “getting migration done in a stable way”, TypeORM seems to be more focused on “doing migration in the right way”. This can be seen from its documentation, there’s a lot of emphasis on “reducing duplication”.&lt;/p&gt;
&lt;h2&gt;Update (8th March 2020)&lt;/h2&gt;
&lt;p&gt;After going with Sequelize and working with it for a few months, here are my latest update on this subject &lt;a href=&quot;/why-i-would-avoid-sequelize-in-the-future&quot;&gt;Why I would avoid Sequelize in the future&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[VS Code, Svelte and Prettier set up]]></title><description><![CDATA[<p>I recently tried to set up a new project to try out Svelte in more depth. I search everywhere but couldn't get VS Code (e.g. on file save) and Prettier CLI (e.g. running <code class="language-text">npm run lint</code>) working at the same time. After hours of trial and error, I finally got it to a place I'm happy with. Hopefully, this will help anyone in a similar position.</p>]]></description><link>https://blog.hao.dev/vs-code-svelte-and-prettier-set-up</link><guid isPermaLink="false">https://blog.hao.dev/vs-code-svelte-and-prettier-set-up</guid><pubDate>Sat, 04 Jan 2020 14:36:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently tried to set up a new project to try out Svelte in more depth. I search everywhere but couldn&apos;t get VS Code (e.g. on file save) and Prettier CLI (e.g. running &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt;) working at the same time. After hours of trial and error, I finally got it to a place I&apos;m happy with. Hopefully, this will help anyone in a similar position.&lt;/p&gt;&lt;h2&gt;Install Dependencies&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm i -D parcel-bundler parcel-plugin-svelte prettier prettier-plugin-svelte svelte&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;svelte&lt;/code&gt; because we want to develop apps with it, duh!
&lt;code class=&quot;language-text&quot;&gt;parcel-bundler&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;parcel-plugin-svelte&lt;/code&gt; &lt;strong&gt;Svelte&lt;/strong&gt; works with all the popular bundler tools such as &lt;strong&gt;Webpack&lt;/strong&gt;, &lt;strong&gt;Rollup&lt;/strong&gt; and &lt;strong&gt;Parcel&lt;/strong&gt;. Personally, I&apos;m a big fan of &lt;strong&gt;Parcel&lt;/strong&gt; so I&apos;ve gone with it here.
&lt;code class=&quot;language-text&quot;&gt;prettier&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;prettier-plugin-svelte&lt;/code&gt; People tend to use a mixture of &lt;strong&gt;Eslint&lt;/strong&gt; and &lt;strong&gt;Prettier&lt;/strong&gt; for code formatting and linting. But I wanted to keep dev dependencies lite, so I&apos;m only using Prettier in this case.&lt;/p&gt;
&lt;h2&gt;Project Structure &amp;#x26; Files&lt;/h2&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;/
|-- package.json
|-- package-lock.json
|-- .prettier
|-- .gitignore
|-- src
     |-- index.js
     |-- index.html
     |-- style.css
     |-- App.svelte
     |-- components
          |-- ...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;package.json&lt;/h3&gt;
&lt;p&gt;When using &lt;strong&gt;Svelte&lt;/strong&gt; with &lt;strong&gt;Parcel&lt;/strong&gt;, one thing to note is to ensure the main property is set to the correct.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-svelte-template&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;private&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;This is not a real repo.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao Dong&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;src/index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;parcel src/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;parcel build src/index.html&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;lint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prettier --check &apos;src/**/*.{html,js,svelte}&apos;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;lint:fix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;prettier --write &apos;src/**/*.{html,js,svelte}&apos;&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;devDependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;parcel-bundler&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^1.12.4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;parcel-plugin-svelte&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^4.0.5&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;prettier&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^1.19.1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;prettier-plugin-svelte&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^0.7.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;sass&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^1.24.2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;svelte&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^3.16.7&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice here we are using &lt;strong&gt;Prettier CLI&lt;/strong&gt; to both check linting and auto-fix linting errors. This means we could both check for linting errors and auto fix them in the terminal by running &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm run lint:fix&lt;/code&gt; respectively. It is a good practice to run at least the lint check in the CI pipeline to ensure code consistency.&lt;/p&gt;
&lt;p&gt;What about VS Code, you might ask. We&apos;ll cover how to set up auto format each time we save a file in later sections. Unfortunately, the CLI auto format and VS Code auto format shares very little in common so will require two different setups. This is also one of the reasons it took me a while to get it working the way I wanted.&lt;/p&gt;
&lt;h3&gt;package-lock.json&lt;/h3&gt;
&lt;p&gt;I&apos;m going to skip package-lock.json since it is a generated file.&lt;/p&gt;
&lt;h3&gt;.prettierrc&lt;/h3&gt;
&lt;p&gt;For &lt;strong&gt;Prettier&lt;/strong&gt; I try to leave most of the formatting options as default, with the following exceptions.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;tabWidth&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;printWidth&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;semi&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;singleQuote&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;trailingComma&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;all&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;.gitignore&lt;/h3&gt;
&lt;p&gt;A very standard git ignore file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;yaml&quot;&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;.DS_Store
node_modules

&lt;span class=&quot;token comment&quot;&gt;# svelte&lt;/span&gt;
.cache
dist

&lt;span class=&quot;token comment&quot;&gt;# dotenv environment variables file&lt;/span&gt;
.env&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;src/index.html&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;UTF-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Svelte with Parcel&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./styles.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./index.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;src/index.js&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; App &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;./App.svelte&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; app &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;App&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;target&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Svelte&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; app&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;src/style.css&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;css&quot;&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;html&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; border-box&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;*,
*:before,
*:after&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;box-sizing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; inherit&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token selector&quot;&gt;body&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;min-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 100%&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;font-family&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -apple-system&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; BlinkMacSystemFont&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Segoe UI&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Roboto&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Oxygen&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Ubuntu&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Cantarell&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Open Sans&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Helvetica Neue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sans-serif&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;scroll-behavior&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; smooth&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;text-rendering&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; optimizeSpeed&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;line-height&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1.5&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #212223&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; flex&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;flex-direction&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; column&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;justify-content&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; center&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;src/App.svelte&lt;/h3&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;script&gt;
    export let name;
&amp;lt;/script&gt;

&amp;lt;style&gt;
    h1 {
        text-align: center;
    }
&amp;lt;/style&gt;

&amp;lt;h1&gt;Hello {name}!&amp;lt;/h1&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;VS Code Set Up&lt;/h2&gt;
&lt;h3&gt;Extensions&lt;/h3&gt;
&lt;p&gt;In order to get the best Svelte support, install the following VS Code extensions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prettier - Code formatter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=JamesBirtles.svelte-vscode&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Svelte&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It might be useful to install these extensions as well, but they are optional.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=fivethree.vscode-svelte-snippets&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Svelte 3 Snippets&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ardenivanov.svelte-intellisense&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Svelte Intellisense&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Settings&lt;/h3&gt;
&lt;p&gt;Within &lt;strong&gt;src&lt;/strong&gt; folder we have 3 main file types that requires linting and formatting:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;*.html&lt;/strong&gt; will be formatted by &lt;strong&gt;Prettier&lt;/strong&gt; (extension installed above, based on &lt;strong&gt;.prettierrc&lt;/strong&gt; config)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;*.svelte&lt;/strong&gt; will be formatted by &lt;strong&gt;Svelte&lt;/strong&gt; (extension installed above)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;*.js&lt;/strong&gt; will be formatted by &lt;strong&gt;Prettier&lt;/strong&gt; (extension installed above, based on &lt;strong&gt;.prettierrc&lt;/strong&gt; config)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In order to get format on file save working, open Settings (JSON) then add the following options:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ...
    &lt;span class=&quot;token property&quot;&gt;&quot;editor.formatOnSave&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;[svelte]&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;editor.defaultFormatter&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;JamesBirtles.svelte-vscode&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then open up the different file types and see what VS Code is showing on the bottom status bar. Notice how both JavaScript and HTML have Prettier ticked, and Svelte doesn&apos;t.&lt;/p&gt;
&lt;img height=&quot;50&quot; src=&quot;//images.ctfassets.net/zsjhz5fgria0/3GsMLnyYwthIAF3N33tqT3/ed7b8cb9990a38f609fcefe81bf316fd/Screenshot_2020-01-04_at_14.27.44.png&quot; alt=&quot;screenshot of vs code formatting html file with Prettier&quot; /&gt;
&lt;img height=&quot;50&quot; src=&quot;//images.ctfassets.net/zsjhz5fgria0/51QIeDqItwSa6VKjRZMSIS/3a5e8156eacfebeaa0c5ca7ea50331b0/Screenshot_2020-01-04_at_14.27.56.png&quot; alt=&quot;screenshot of vs code formatting javascript file with Prettier&quot; /&gt;
&lt;img height=&quot;50&quot; src=&quot;//images.ctfassets.net/zsjhz5fgria0/7hQ2XFSGzo1m1MVbtVqdIJ/f725889d4d03f441d2c7a7faff0e91c7/Screenshot_2020-01-04_at_14.28.12.png&quot; alt=&quot;screenshot of vs code formatting svelte file with Svelte&quot; /&gt;
&lt;p&gt;As mentioned above the &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm run lint:fix&lt;/code&gt; will work with Svelte file types no problem. However, Prettier in VS Code for some reason just doesn&apos;t want to auto format Svelte files. So I left the auto format to VS Code&apos;s Svelte extension instead of the Prettier extension.&lt;/p&gt;
&lt;h2&gt;Additional Reading&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.rockyourcode.com/prettier-and-es-lint-setup-for-svelte-js/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.rockyourcode.com/prettier-and-es-lint-setup-for-svelte-js/&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Software developers' learning dilemma]]></title><description><![CDATA[<p>The world of tech is moving fast, maybe too fast! Trying to keep up with everything is stressful, to say the least, might even call it impossible to be realistic. There are a countless number of frameworks and libraries being update each day and new ones being released as well. Yes, maybe is more of a JavaScript problem, but the same problem more or less applied to other programming languages as well. So how can we find time to enhance our existing skills to be a better developer, while the backlog of learning constantly growing? We are being bombarded with information each day, sooner or later we'd miss demotivated and lose the urge to learn. I've gone through all those stages and feel I have a pretty good balance on how to learn daily but not risk burn out.</p>]]></description><link>https://blog.hao.dev/software-developers-learning-dilemma</link><guid isPermaLink="false">https://blog.hao.dev/software-developers-learning-dilemma</guid><pubDate>Mon, 02 Dec 2019 01:18:00 GMT</pubDate><content:encoded>&lt;p&gt;The world of tech is moving fast, maybe too fast! Trying to keep up with everything is stressful, to say the least, might even call it impossible to be realistic. There are a countless number of frameworks and libraries being update each day and new ones being released as well. Yes, maybe is more of a JavaScript problem, but the same problem more or less applied to other programming languages as well. So how can we find time to enhance our existing skills to be a better developer, while the backlog of learning constantly growing? We are being bombarded with information each day, sooner or later we&apos;d miss demotivated and lose the urge to learn. I&apos;ve gone through all those stages and feel I have a pretty good balance on how to learn daily but not risk burn out.&lt;/p&gt;&lt;p&gt;I feel that I go through these phases at least every few months. It is probably quite obvious now I blog, from Sep to Nov 2019 has been a tough time for me. This is accurately reflected by the number of blog posts released in this period. At the end of Nov I finally managed to pull myself back out of this slump state, unfortunately, I know it is just a matter of time before it happens again. I&apos;m sure this is not just a problem for me, so I want to talk about it in this blog post.&lt;/p&gt;
&lt;h2&gt;The dilemma&lt;/h2&gt;
&lt;p&gt;When I was still at University I didn&apos;t do much learning through blogs, etc. I found that lecture notes, recommended reading and course works were enough to keep me busy. There was one lecturer who recommended 10 books and what seemed like an infinite list of papers to read for her module. I am going to be honest and say that I read none of it. If you are wondering how I did in the exam, let&apos;s just say I deserved the exam marks 😂. Now looking back I think it was the amount of reading that scared me away, I kept pushing it back and then before I knew it exam day came.&lt;/p&gt;
&lt;p&gt;Now I am no longer told I have to do this kind of reading, but I feel like the same problem persists in a developer&apos;s everyday work life. Let&apos;s face it, there is a lot to do and think about. The more curious and responsible you are as a developer, the more the mountain of things to do and learn grows. I am interested in many roles in a product team, I love to learn about UX (including UI), I want to know the business requirements from a product point of view and not just something to code against, I also want to make sure I&apos;m playing a useful role in my team (help to add tickets to backlogs, etc). That is a lot of stuff to do if you break it down and slot it into everyday work time. Then there are meetings and doing the work I&apos;m paid to do (duh!). So when is the time to learn about &lt;code class=&quot;language-text&quot;&gt;sveltejs&lt;/code&gt; or other frameworks and libraries? I&apos;m ashamed to say that I even write down learning other languages as part of my learning goals.&lt;/p&gt;
&lt;p&gt;Assume you somehow find the time to learn things at work, or you find the motivation to sit down and do self-study after work. The next question that comes to mind is &quot;what should I learn&quot;? Only to find all sorts of negative emotions flooding my brain. How can you identify if something is useful or a waste of time, when there are tens of thousands of pieces of information are thrown your way? &quot;Deep dive in computer algorithms&quot;, &quot;Machine Learning: an introduction&quot;, &quot;AI: from zero to hero&quot;... There is no way for you to consume faster than content being produced. There are YouTube videos, blog posts, podcasts, books, new libraries, new frameworks, new languages, tweets, Linkedin posts and what feels like a million other things to digest each day. They all have click-baity title, they all seem relevant and important to know. Just saving it to read later isn&apos;t helpful either, it just kicks starts a vicious cycle. The more we bookmark and save, the more negative we feel. The thought of &quot;oh my god when will I ever find enough time to consume all these&quot; becomes an anchor weighing us down more and more.&lt;/p&gt;
&lt;p&gt;Just explaining this stuff makes me want to smash my computer, quit my job and go live in the woods somewhere. This information overload is not healthy. It brings anxiety and stress with it, which is why every-so-often I enter the slump. I should think of a cool phrase for this feeling and call it something like &quot;Drowning in information, gasping for air&quot;.&lt;/p&gt;
&lt;h2&gt;The balance&lt;/h2&gt;
&lt;p&gt;It doesn&apos;t take a genius to see that we need a balance, better way of consuming knowledge coming our way. At least this is what we need to do before a magical AI comes along and feed us the right information to learn at a steady rate. My most recent slump just shows that I haven&apos;t figured everything out yet, but here are some of my thoughts:
On one hand we have super chilled people, they never seem stressed about anything. They would deliver work at a slow pace, causing inconveniences for both their team and maybe other teams. They regularly take days off to recharge, and probably will never burn-out. I like their attitude to life, no doubt they will be very happy. But different people look for different things in life, I want to be reliable and bring value.
On the other hand, some people spend at least one day of their weekend maybe longer coding and learning. People build cool libraries, maintain multiple open source projects, and then have side hustles that bring in some extra cash. I would look at them and think to myself &quot;I could I could be this motivated&quot;, but I know realistically I won&apos;t be able to do what they do.
So how should I find my balance? I started listening to podcasts and read self-help books. It is ironic, to answer the question &quot;how should I learn things&quot; I must first learn about it. Here are some useful materials:
Getting Things Done by David Allen - Taught me to organise my life using todo lists. One of the key messages is that by writing things down and keeping it somewhere we know we will review regularly, it will reduce anxiety.
Essentialism by Greg Mckeown - It talks about concentrating on things that are most essential in your life, only this way will there be peace and balance in your life (pretty deep stuff).
Pareto principle (the 80:20 rule) - The Pareto principle states that roughly 80% of the effects come from 20% of the causes. These means don&apos;t be a perfectionist, one way consuming more stuff is to spend 20% of the time skim through an article getting 80% understanding of the content than to read it word for word.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;These three points are by far some of my most important learnings in the past few years. I hope people find them useful. As mentioned already, even after adopting the above advice I&apos;m still struggling to find the right balance. Maybe that is okay, life is short we should enjoy things other than improving our coding skills. Even for those wanting to speed up their career, sometimes losing yourself for a while helps to find the &quot;real&quot; direction.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Awesome tools to use in personal projects]]></title><description><![CDATA[<p>A developer who doesn't work on personal projects isn't a true developer! Just kidding! But working on small side projects is an awesome way to solidify and improve our craft. It is both practice and exploration, this is the best opportunity for you to pick up on something others might not know about! In this blog post, I will walk through some of my favourite tools and what I feel is missing in my toolbox.</p>]]></description><link>https://blog.hao.dev/awesome-tools-to-use-in-personal-projects</link><guid isPermaLink="false">https://blog.hao.dev/awesome-tools-to-use-in-personal-projects</guid><pubDate>Sun, 01 Dec 2019 22:13:00 GMT</pubDate><content:encoded>&lt;p&gt;A developer who doesn&apos;t work on personal projects isn&apos;t a true developer! Just kidding! But working on small side projects is an awesome way to solidify and improve our craft. It is both practice and exploration, this is the best opportunity for you to pick up on something others might not know about! In this blog post, I will walk through some of my favourite tools and what I feel is missing in my toolbox.&lt;/p&gt;&lt;p&gt;I often have crazy product or app ideas, most of which I don&apos;t carry through with. Why? It takes too much time to code up and then there&apos;s the maintenance later on. In the back of my mind, I&apos;m always looking for awesome products that could solve these problems for me. Luckily over the years, I have found many of them, and each time I find a new tool that makes the process smoother I find myself finishing more products.&lt;/p&gt;
&lt;h2&gt;What I have found so far&lt;/h2&gt;
&lt;p&gt;The best part about all the following tools is that they all have a free tier. If you are planning to use them for personal use, it&apos;s extremely unlikely to go over the allowance. If you do end up going over, then your project must be super successful, in which case, congrats!&lt;/p&gt;
&lt;h3&gt;Best hosting &amp;#x26; CI/CD tool - Netlify&lt;/h3&gt;
&lt;p&gt;I enjoy learning about DevOps, but I never intend to become a DevOps engineer. I enjoy creating and building products, DevOps is too far from the customer for my liking. I also found CI/CD of any personal projects to be a pain point, it takes way too much time to set up that&apos;s not even mentioning the cost of running and maintaining your CI/CD solution. A few years ago, I found out about Netlify. It opens the door to many of my projects. This blog is one of them, it makes many of the challenges around hosting and deploying so easy. A few clicks later I can start working on my new project.&lt;/p&gt;
&lt;h3&gt;Development tools - CodeSandbox, Gitpod, VS Code&lt;/h3&gt;
&lt;p&gt;Isn&apos;t it annoying when you just want to brain dump some code and you have to go through all of the project setups? I started using CodeSandbox for any small or app prototypes. All it takes is selecting a template project to get started, then I can just jump in and start coding right in my browser. It is amazing!&lt;/p&gt;
&lt;p&gt;Then there are some established projects, making small changes requires clone projects from Github, etc. It is so much hassle and I&apos;d just postpone it time and again. Recently I found this tool called Gitpod which solves this exact problem, it could check out any repository and allow you to work on it straight from your browser. You could run terminal commands, so it is possible to build your projects as if you are developing on your laptop.
Finally for any serious projects I normally would git clone the repository on to my laptop and work on it with VS Code. I have a list of extensions which empowers my development experience. But I tend to only use VS Code for projects I know I&apos;ll be working on for a long time.&lt;/p&gt;
&lt;p&gt;I have written about VS Code keyboard shortcuts since all three tools mentioned in this section is based on VS Code, so most of these keyboard shortcuts can be used with all three products!&lt;/p&gt;
&lt;h3&gt;CMS - Contentful&lt;/h3&gt;
&lt;p&gt;For a long time, I struggle to find an effective way of building my blog. Until I found out about Contentful which is a headless CMS. It is perfect for serving data for my blog! I have yet to find another use case to use it in my other personal projects. There are also many other headless CMS out there, I just happened to have picked Contentful when it was the most popular. It has been a couple of years, there could well have been newer and better headless CMS out there!&lt;/p&gt;
&lt;h2&gt;What is still missing - user data storage&lt;/h2&gt;
&lt;p&gt;All the above products have been a godsend, they have made my development process so much easier. Since I tend to be the only one working on my personal projects, so their tools make it easy to get started on an idea and release something in a few days.&lt;/p&gt;
&lt;p&gt;However, I am still searching for one more thing. There are tones of apps I want to create that could do with some kind of storage in a server (instead of storing in Local Storage). I have created several apps that utilise Local Storage so I have nothing against it. I just find it annoying that all the data were unavailable when I switch between devices.&lt;/p&gt;
&lt;p&gt;I would love for there to be a product that could:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Store individual user&apos;s data&lt;/li&gt;
&lt;li&gt;Have some kind of authentication in place so only the user themselves could access their data (maybe me as the developer as well for tech support). It would be ideal if this product could allow client-side direct access, without the need for a backend server doing data validation and authentication.&lt;/li&gt;
&lt;li&gt;Easy to set up, like have a data store ready with a few clicks&lt;/li&gt;
&lt;li&gt;A free tier, yes I&apos;m cheap like that! But seriously, I don&apos;t want to be paying for tens and hundreds of personal projects. They will have minimal usage anyway, so a free tier would be amazing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have look around and there isn&apos;t a product that could do the above well. The best one I have found so far is Google&apos;s Firebase. It almost nails all of the above points, with the only exception being authentication. I found it very difficult (and confusing might I add) to set data store rules.&lt;/p&gt;
&lt;p&gt;I don&apos;t want to spin up a new database and create a bunch of APIs every time I have created a small app. This data storage problem is the last one to solve for me. If anyone knows any products I am not aware of, please get in touch I would love to know!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Linux is still Linux in 2019]]></title><description><![CDATA[<p>The experience of installing Linux and getting it running on a machine is just as inconsistent as a year ago and years before that. There were visible improvements, but still plenty room for improvements compared to Windows and Mac OS. I encountered many issues during my little experiment, I ended up switching back to my old setup at the end.</p>]]></description><link>https://blog.hao.dev/linux-is-still-linux-in-2019</link><guid isPermaLink="false">https://blog.hao.dev/linux-is-still-linux-in-2019</guid><pubDate>Fri, 08 Nov 2019 17:36:00 GMT</pubDate><content:encoded>&lt;p&gt;The experience of installing Linux and getting it running on a machine is just as inconsistent as a year ago and years before that. There were visible improvements, but still plenty room for improvements compared to Windows and Mac OS. I encountered many issues during my little experiment, I ended up switching back to my old setup at the end.&lt;/p&gt;&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;I had a few days off last week and didn&apos;t book any holidays to go anywhere. So the nerd in me thought it would be a great idea to get Windows and Linux dual boot setup on my desktop. I was looking forward to the new improvements which were added in the past year or so. The result is somewhat disappointing, and I ended up switching back to Windows (I use a MacBook Pro for most of my coding tasks). In this post I&apos;ll go over some of the issues I faced, hopefully, it will show why I decided to switch back.&lt;/p&gt;
&lt;p&gt;Before I get started, I just want to say the following as disclaimer. I ended up struggling with the setup process for 3 evenings in a row, trying to get a good Linux distribution dual booting on my Windows desktop. I have experienced a lot of weird issues and it is frustrating, to say the least. So naturally, this post will contain quite a lot of negative feelings. But I don&apos;t want people to think this is me attacking Linux for being worse than Mac OS and Windows. I just had so much hope and expectations going in, it was quite a disappointing experience.&lt;/p&gt;
&lt;p&gt;As part of my little experiment, I tried the following distributions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pop!_OS (19.10, released 22nd October 2019)&lt;/li&gt;
&lt;li&gt;Deepin (15.11, released on 19 July 2019)&lt;/li&gt;
&lt;li&gt;Elementary OS (5.0 &quot;Juno&quot;, released on 16 October 2018)&lt;/li&gt;
&lt;li&gt;Zorin (OS 15, released on 5 June 2019)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Every distribution has its problems&lt;/h2&gt;
&lt;p&gt;I tried the latest version of the above distributions, but as you can see from their release dates. They range from Oct 2018 to Oct 2019. These release dates nicely reflected the state of these distributions. &lt;/p&gt;
&lt;h3&gt;Elementary OS&lt;/h3&gt;
&lt;p&gt;Elementary OS was the most stable one out of all of them, but it also meant there was a lack of new features.&lt;/p&gt;
&lt;p&gt; &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6HscNpXNpHluAG1NHxP2Zw/2a250f2587d9249acc5f8e3bcf5e0231/Untitled-26e7e235-ba0f-4ede-a1c0-2b8337c6805b.png&quot; alt=&quot;Untitled-26e7e235-ba0f-4ede-a1c0-2b8337c6805b&quot;&gt;  &lt;/p&gt;
&lt;p&gt;Elementary OS used to be the best-looking distribution a couple of years ago. Since the latest version is from a year ago, all other distributions look more modern and simply better.&lt;/p&gt;
&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lack of Chinese Input - This distribution is the oldest out of all the ones I tried. It isn&apos;t based on Ubuntu 19.10, therefore, it doesn&apos;t have built-in Chinese input. For others, they might not care, but for me, this is also a deal-breaker.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apart from the fact it was missing all the new features from 19.04 and 19.10 releases, there is little to complain about it. I decided not to go with it because it didn&apos;t have native support for Chinese input. Which is a deal-breaker for switching to it completely? When the next version of Elementary OS comes out based on Ubuntu 19.10 or later, then it would be the best choice for me I think.&lt;/p&gt;
&lt;p&gt;Note: there are a lot of activities on Github, hopefully, it won&apos;t be long now.&lt;/p&gt;
&lt;h3&gt;Pop!_OS&lt;/h3&gt;
&lt;p&gt;A more recent distribution compared to the likes of Elementary OS. But it is backed by a company called System76, who specialises in building and selling Linux laptops. It claims to have very good gaming support out of the box, now with the new Nvidia driver improvements brought in as part of Ubuntu 19.10 (which the latest version is based on), it should be one of the best choices for people.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7noURsjwKf3SNeBgH3W1SO/e31d441e887c38a2203c2d26ce82bd78/Untitled-2520c3fa-a901-4bad-a308-a00edc2f201d.png&quot; alt=&quot;Untitled-2520c3fa-a901-4bad-a308-a00edc2f201d&quot;&gt;&lt;/p&gt;
&lt;p&gt;Notice the keyword &quot;should&quot;. I spent the most time trying to get this distribution working because I felt like it was &quot;the one&quot;. It got so much right and has so much potential!&lt;/p&gt;
&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Lack of fractional scaling - Since it uses Gnome desktop by default, this means it doesn&apos;t support fractional scaling without turning on beta flag via Terminal. Out of the box a user could choose between 100%, 200%, 300%, etc scaling options. 100% is too small on 4k monitors and 200% is way too big in my opinion.&lt;/li&gt;
&lt;li&gt;Auto power off monitor resets fractional scaling back to 100% - After turning on beta fractional scaling option, I left the machine idling for a bit. After 5 min (system default option) it turned the monitor off when I woke the machine again it changed the scaling back to 100%. The only way for me to work around this is to disable auto turn off the monitor.&lt;/li&gt;
&lt;li&gt;Login screen scaling cannot be changed - No matter what I changed the fractional scaling value to, I could never get the login screen to change from the default 100% scaling level.&lt;/li&gt;
&lt;li&gt;Short scroll -  I had really weird mouse scrolling speed. It would take me down 5 lines of text on a 4k monitor at 150% scaling when I&apos;d normally expect the whole screen to be scrolled down. Maybe it could be fixed by changing scroll input values in the Terminal, but I have given up on this distribution at this point.&lt;/li&gt;
&lt;li&gt;No dual boot with Windows - No matter how I configured my BIOS, I simply couldn&apos;t get Windows boot option to show up during boot. I even reinstalled both Windows and Pop!_OS multiple times as a result. Maybe this is due to I&apos;m trying to dual boot Windows and Linux on different SSD hard disks. I didn&apos;t figure it out at the end.&lt;/li&gt;
&lt;li&gt;Untrusted sources - This is another problem that just left a bad taste in my mouth. After booting into Pop!_OS, I wasn&apos;t able to add quite a few sources. Every time I try, I just get the error message cannot look up untrusted sources (or something like that). It seems that 19.10 is so new, there are many packages which haven’t been released yet. Long story short it is not possible to pull these packages down, or at least with everything I tried I was not able to get it working.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Deepin&lt;/h3&gt;
&lt;p&gt;Deepin UI looks very sleek, but after trying it out it seems a little too flashy for my taste. The settings menu on the side is not easy to use in my opinion, it should have just kept it simple like the other distributions, but it&apos;s just my preference.&lt;/p&gt;
&lt;p&gt;A big plus for me is the fact the app store is the best out of all of the distributions I have tried. It had everything I needed, even tools such as &lt;code class=&quot;language-text&quot;&gt;VS Code&lt;/code&gt; which 9 times out of 10 I need to manually install in the Terminal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3WXdDOXkIFd4tOIskE6p36/284abc5c33e80154e18a5c13cc99a961/Untitled-d29a8295-dde5-4bac-9287-cf81a01ee0f6.png&quot; alt=&quot;Untitled-d29a8295-dde5-4bac-9287-cf81a01ee0f6&quot;&gt;&lt;/p&gt;
&lt;p&gt;The OS is customisable, maybe a little too much. Spending too much time and energy means a lot of areas in the OS itself feels very rough around the edges.&lt;/p&gt;
&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Poor performance - The performance is one of the worst I have experienced on Linux. When opening, hiding and closing apps everything just feels a lot slower than other distributions.&lt;/li&gt;
&lt;li&gt;Confusing UI - The problem of spending too much effort on making everything look too fancy is that as the user you&apos;ll sometimes find small UI issues when using the OS normally.&lt;/li&gt;
&lt;li&gt;Inconsistent UX - There are really weird things embedded in the OS, it feels like lots people build different parts then glued it all together.&lt;/li&gt;
&lt;li&gt;Bad Chinese input - this had Chinese input support, but it is so buggy they may as well excluded it. It got to a point where I couldn&apos;t switch back to English 🤦‍♂️&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Zorin&lt;/h3&gt;
&lt;p&gt;Zorin by far is the best-looking distribution out there in my opinion. If you upgrade to the paid version, then there are even more type of layouts to pick from e.g. Mac dock layout. UI and UX just looking amazing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/BN00IZPzKxQNUUgLNrC2b/18e58b53aa2304b33b67f1482c143702/Untitled-167bf7f2-6799-4289-8b7d-dfa02a95943e.png&quot; alt=&quot;Untitled-167bf7f2-6799-4289-8b7d-dfa02a95943e&quot;&gt;&lt;/p&gt;
&lt;p&gt;Issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Terrible performance - This is the worst performing distribution out of everything I tried to-date. Even dragging windows around it would lag like hell. It felt like an animation problem, but when I checked GPU settings everything looked fine. It detected by GPU and everything.&lt;/li&gt;
&lt;li&gt;Lack of Chinese Input - Similar to other distributions, this isn&apos;t based on Ubuntu 19.10, therefore, it doesn&apos;t have built-in Chinese input.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I didn&apos;t play around with this distribution enough to find other issues, because of how bad the performance was.&lt;/p&gt;
&lt;h2&gt;Gaming&lt;/h2&gt;
&lt;p&gt;Gaming has always been an issue on Linux. Considering this is a problem for all distributions I thought a dedicated section makes more sense.&lt;/p&gt;
&lt;p&gt;Almost every other month I&apos;d read someone writing that they have fully switched to Linux and the gaming experience is much better than before. I could see their point, with tools such as Lutris it is much easier to install games. Unfortunately, it is still no Windows. If you are lucky you might be able to get some game running, but major OS update might break the game and a game update might break the game. There is so much uncertainty when you are you in a mood for gaming you might find yourself doing debugging instead.
This is why I wanted to dual boot Linux with Windows instead of switching over completely. The truth is I was right when I tried to get League of Legends (one of my most played game) working on Pop!_OS. I could install and boot into the game with Lutris using Wine no problem, but every time I start playing it would crash. I&apos;m not saying this is not possible to fix, but it is too much for me when I just want to play a couple of games and relax.&lt;/p&gt;
&lt;h2&gt;The competitions are too strong&lt;/h2&gt;
&lt;p&gt;I got WSL 2 (Windows Subsystem for Linux) working on my desktop before I tried to do all this. Sure it is not perfect, which is why I wanted to get Linux running on my desktop. Though it is not perfect, and I can see myself running into problems in the future. It is a lot less maintenance than dual booting Linux with Windows.&lt;/p&gt;
&lt;p&gt;My perfect development machine is still a MacBook Pro right now. I just find it superior in so many ways compared to Windows and Linux. &lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;To summarise, compared to a year or two ago there have been huge improvements. I&apos;m quite excited about new competitors such as Zorin, Deepin and Pop!_OS. Whilst Linux Mint, Ubuntu, etc are still leading the pack in terms of downloads and interests, I see a lot of potential in these new challengers.&lt;/p&gt;
&lt;p&gt;Now, if you are thinking of installing or switching to Linux, my recommendation is to stay put with Mac OS and Windows. Unless you want to tinker with the OS, in my opinions these distributions are not quite ready to be used as Mac OS and Windows replacements for productivity. No matter what I did, I found myself more or less had to settle with some compromises.&lt;/p&gt;
&lt;p&gt;I love the fact Linux is open and free. They aren&apos;t just Operating Systems on people&apos;s personal computers but runs most of the cloud too. Linux has a special place in my heart, it is a rare and positive thing in the world of technology and software development (with all the spying and privacy leaks).&lt;/p&gt;
&lt;p&gt;I am still looking forward to switching to Linux one day. Maybe I&apos;ll give it another year and try again? We&apos;ll see...&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Creating TV Apps With Web Technology]]></title><description><![CDATA[<p>Ever wondered how TV apps are made? How it differs from the usual web app or mobile app development? I was fortunate enough to work on TV platforms for about year now, and there is some exciting stuff for me to share around this subject. I will first give an introduction on TV platforms, then explain why we decided to use web technology to build TV apps, finally I'll go through some of the challenges we faced that are specific to TV app development.</p>]]></description><link>https://blog.hao.dev/creating-tv-apps-with-web-technology</link><guid isPermaLink="false">https://blog.hao.dev/creating-tv-apps-with-web-technology</guid><pubDate>Mon, 26 Aug 2019 18:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Ever wondered how TV apps are made? How it differs from the usual web app or mobile app development? I was fortunate enough to work on TV platforms for about year now, and there is some exciting stuff for me to share around this subject. I will first give an introduction on TV platforms, then explain why we decided to use web technology to build TV apps, finally I&apos;ll go through some of the challenges we faced that are specific to TV app development.&lt;/p&gt;&lt;p&gt;As mentioned already I&apos;ve broken down the blog post into different sections, feel free to skip to the relevant sections if you are not interested in the background information.&lt;/p&gt;
&lt;h2&gt;Introduction to TV platforms&lt;/h2&gt;
&lt;p&gt;Many people own at least one TV. It is what we use to play console games, watch the latest shows and movies. We can even project our phone or computers on to them to show things to a bigger group. It is estimated that 259 million TV units will be solved by 2020 &lt;a href=&quot;https://www.statista.com/topics/4761/smart-and-connected-tvs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;source: statista&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here are just some of the major TV platforms that we support:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1zWZ94Iln3rpsgcwtkk57i/3fd0b1f6e8178ac7b4e73df41e0bc00e/Screenshot_2019-08-26_at_17.33.46.png&quot; alt=&quot;many tv platforms&quot;&gt;&lt;/p&gt;
&lt;p&gt;As shown in the image above, the platforms are made out of a mixture of different type of devices. Some platforms are built into TVs (e.g. Samsung&apos;s Tizen, LG&apos;s webOS, Android TV), there are also external boxes that you can plug into any TVs and turn them into a smart TV (e.g. Amazon Fire Stick, Apple TV, Android TV again, Chromecast, etc), then there are game consoles that can act as a TV box on top of its gaming functionalities. Finally, streaming platforms often come up with their own dedicated physical devices and operating systems (Roku, etc). It is probably safe save to say, the fragmentation within TV platforms are a lot worse than web or mobile.&lt;/p&gt;
&lt;p&gt;The above platforms are also known as &quot;smart-TVs&quot; by the customer, why is it smart? Because they have apps.&lt;/p&gt;
&lt;h2&gt;Building TV Apps&lt;/h2&gt;
&lt;p&gt;It is quite unfortunate for companies who needs to release apps on TVs, because of the fragmentation issue mentioned. So how might one go about developing a TV app? It is a bit like Mobile app development actually, there are 3 general approaches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Native&lt;/li&gt;
&lt;li&gt;Web or PWA&lt;/li&gt;
&lt;li&gt;Hybrid&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let&apos;s talk about each of them and see what are the pros and cons.&lt;/p&gt;
&lt;h3&gt;Native Apps&lt;/h3&gt;
&lt;p&gt;In order to go Native, we will need to build a separate package (app bundle) for each platform. Give the number of platforms we need to support, this is both time-consuming and costly for the business. Especially when most of the platforms use different languages with their unique frameworks and libraries. The development process involves writing redundant business logic in different languages which are not shareable. Needless to say, this is very inefficient and should be avoided if possible.&lt;/p&gt;
&lt;p&gt;Mobile development has a strong argument to go Native because there are only 2 platforms for them to develop against. Imagine if there are 10 platforms, in that case I doubt many companies can afford to build their app in Native.&lt;/p&gt;
&lt;p&gt;On the positive side, out of all the choices, building dedicated Native apps for each platform gives the most customizability and control. If would be possibel to adapt apps to their respective platforms, or even make them totally different from each other if we wish.&lt;/p&gt;
&lt;h3&gt;Web App or PWA&lt;/h3&gt;
&lt;p&gt;Surprisingly this is more viable than one might think. Most platforms support some kind of WebView which are used to render web applications. It is how Progressive Web Apps (PWA) work on Android (mobile). However, the main drawback with this approach is the lack of native API support. Say I want to detect when the TV loses internet connection then show an error notification as it happens. A pure Web App won&apos;t be able to achieve that as most TV platforms don&apos;t bake it into its WebView. These kind of limitations means it is difficult to build a high-quality app, and in some cases, a basic application.&lt;/p&gt;
&lt;p&gt;The main advantage of this approach is how easy it would be to deploy apps to all the platforms since all we have to do is to deploy the web app and then register it on each platform. Unfortunately, this approach is not ready just yet.&lt;/p&gt;
&lt;h3&gt;Hybrid Solution&lt;/h3&gt;
&lt;p&gt;This solution effectively combines the benefits of the first two approaches. With this we create a basic shell application for each native platform, they all have limited functionalities and provide WebView with native APIs support. Inside the shell app, there is a web app that contains most of the app logic. This web app is shared across all platforms, which means less redundant code.&lt;/p&gt;
&lt;p&gt;Here&apos;s a diagram to better illustrate how it all works:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5KPmVYXIYP7XjE4c0WzKLs/56d2d1ec3b904d0ed67e8c2ddca3ff7b/Screenshot_2019-08-26_at_18.06.06.png&quot; alt=&quot;hybrid solution diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;In this example, the shell application is written in Java and uses Cordova to provide native API to WebView.&lt;/p&gt;
&lt;h2&gt;TV Platform Specific Challenges&lt;/h2&gt;
&lt;p&gt;Before we drive into the challenges, let me first show you what kind of UX we are working with. The image below shows an out-dated design because the new design is still sensitive information.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6KJ3hFwuiCTMCh79STmYMx/cb09aa2f69368106368d1699510b821b/Screenshot_2019-08-26_at_18.10.47.png&quot; alt=&quot;TV app UI design&quot;&gt;&lt;/p&gt;
&lt;p&gt;Note the following terminologies, we&apos;ll be referring back them a lot in this section.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Tile - a rectangle block&lt;/li&gt;
&lt;li&gt;Rail - a row of Tiles&lt;/li&gt;
&lt;li&gt;Grid - multiple rows of Rails&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Display&lt;/h3&gt;
&lt;p&gt;TV devices are meant to be used from far away, this means we had to make all the Tiles large enough so that the text is still readable from a few metres away. This leads to less information is shown on the screen, in turn, it would require more user actions to browse around. This mean all of our UI had to be simple and concise.&lt;/p&gt;
&lt;p&gt;TVs come in different screen sizes (luckily the same ratio). How can we make the UI elements the right size for all devices no matter the screen size? We ended up going with &lt;a href=&quot;https://css-tricks.com/fun-viewport-units/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Viewport units&lt;/a&gt; to resolve this issue. But it is still challenging to use a percentage based unit, especially when we want to add animation to something often have to use values with long decimal points.&lt;/p&gt;
&lt;h3&gt;Navigation&lt;/h3&gt;
&lt;p&gt;Navigation is a core part of any application, without it, there isn&apos;t much the user could do with the app. Unlike web or mobile, we can&apos;t click or tap on stuff to register an action. Instead, we are limited to TV controller inputs, again due to the fragmentation problem identified already, we want to keep things simple here. We support these actions: UP, DOWN, LEFT, RIGHT, SELECT, and BACK. The whole app behaviour needs to be built around these basic interactions and this brings some serious challenges.&lt;/p&gt;
&lt;p&gt;Focus is a big thing, so I&apos;ve dedicated a whole section for it later on.&lt;/p&gt;
&lt;p&gt;With browsing all controlled by arrow keys. Each button press equates to one user action. If there is a long Rail, it will take tens of button presses to browse through all of Tiles which is not ideal. To improve the user experience here, we created &quot;fast scroll&quot; functionality. When the user press and holds a button (e.g. LEFT), this allows faster navigation around Rails (they will see the tile shifting through) until they release the button.&lt;/p&gt;
&lt;p&gt;With Grids, we have the same problem as long Rails. The &quot;fast scroll&quot; technique is not going to help us much here, because now we are working with 2 dimensions horizontal and vertical navigation. To help users find what they want on the grid faster, we introduced the &quot;wrap-around&quot; concept, which means when the user press LEFT on the most left Tile, the focus will wrap around to the Tile on the far right side. Using the screenshot example above, when user press LEFT on the current focus Tile &quot;Milan v Atalanta&quot; the focus will shift to &quot;Roma v Sampdoria&quot;.&lt;/p&gt;
&lt;h3&gt;Layered Views&lt;/h3&gt;
&lt;p&gt;It is worth knowing the structure of the app before we proceed to Focus management. The app we are building has 4 layers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Player layer - video player&lt;/li&gt;
&lt;li&gt;Page layer - shows rails&lt;/li&gt;
&lt;li&gt;Overlay layer - transparent and appears on top of Page layer&lt;/li&gt;
&lt;li&gt;Notification layer - for error handling, acting as modals&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5RD2jtM5iwHByVqwjnyM1P/6ba19572b193f8afd22b1d63f5373184/Screenshot_2019-08-26_at_18.52.18.png&quot; alt=&quot;layer diagram&quot;&gt;&lt;/p&gt;
&lt;p&gt;The reason why we have this multiple-layer design is that the user must use the buttons specified in the Navigation section to move around the screen. User can navigate back and forth between the different layers using SELECT and BACK buttons. When the user change between different layers, the focus will update and refocus on something appropriate based on what layer and content are being displayed. Therefore the layered design, will give the user a sense of depth on an otherwise very flat navigation flow.&lt;/p&gt;
&lt;h3&gt;Focus Management&lt;/h3&gt;
&lt;p&gt;Focus is a crucial part of the UX since we need a way to indicate to the user what they are focusing on. This was by far one of the most challenging parts for us.&lt;/p&gt;
&lt;p&gt;One of the reasons that make focus difficult to manage is that we require one and only one thing in focus at any point in time. Otherwise we either risk multiple elements in focus at the same time, or that focus was completely lost with the user stuck in their journey.&lt;/p&gt;
&lt;p&gt;We had to ensure focus can be handed around the app correctly. This means between elements, pages, layers and even deep-linking. How do we know what to give focus to and focus on? To solve this problem, we create a focus map (JavaScript object) which we use to look up where the focus should go in different situations. E.g. given the page, currently focused element, keypress we can find out what to focus on next.&lt;/p&gt;
&lt;p&gt;For deep-link journeys on TVs, when the user lands on a page with no prior focus, the app will assign focus to the default focusable element. This &quot;default focus&quot; is part of the focus map explained above.&lt;/p&gt;
&lt;h3&gt;Back Journeys&lt;/h3&gt;
&lt;p&gt;This is another topic that gave the whole team a lot of headaches. Put simply, the back journey is what happens when the user press the BACK button. I think the main challenges here comes from the UX and not the TV platforms. But though I&apos;d mention it anyway, just because it caused us a lot of pain.&lt;/p&gt;
&lt;p&gt;Back journey could mean any one of these things (knowing how complex it is, I probably missed some out):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It could be simply reset focus on the current page, often going back to the default focusable element&lt;/li&gt;
&lt;li&gt;It could close or dismiss notification without affecting Browser History (no routing change)&lt;/li&gt;
&lt;li&gt;Go between Overlay and Page layers (change in Browser History)&lt;/li&gt;
&lt;li&gt;Go between different Pages (change in Browser History)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;On any BACK button press, it could result in one of the above behaviours. The difficulty is working out which one, and it depends on several factors: where the user came from, where the user is right now, what is in focus or is it part of any specific business logic?&lt;/p&gt;
&lt;p&gt;There are talks of change the journey completely. &lt;/p&gt;
&lt;h3&gt;Performance&lt;/h3&gt;
&lt;p&gt;The TV devices are such a mixed bag, some are quite powerful while others are very limited in resources. Performance optimisation is one of those conversations we have weekly. But I won&apos;t cover more of it here, if you are interested please check out my previous posts &lt;a href=&quot;/improving-css-performance-of-cordova-apps-on-android-tvs&quot;&gt;improve CSS performance of Cordova apps on Android TVs&lt;/a&gt; and &lt;a href=&quot;/ways-to-improve-list-loading-performance&quot;&gt;ways to improve list loading performance&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Development&lt;/h3&gt;
&lt;p&gt;We didn&apos;t want the Native side of things slow us down when we develop the web application. So we made sure that we can spin up the web app in the browser and develop it the same (almost) way as a standard web application. We use hot-module-reloading and other common development strategies, so we can implement new features at roughly the same speed.&lt;/p&gt;
&lt;p&gt;Once we finish developing a feature, we always have to test it on physical devices. This almost always means generating a new Native Shell app and side-load the web app inside, then test it on whatever device we are developing against.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;The actual project I am working has a lot of complexities than the ones mentioned above, because we are using a pretty complex micro-frontend architecture and teams in different countries work on different part of the architecture. But I still enjoyed building TV apps with web technology, there are some interesting findings along the way which helped me to understand more about frontend development in general. If anyone gets an opportunity to work on TV platforms, based on my own experience I&apos;d recommend people give it a try!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Micro Frontend: An Introduction]]></title><description><![CDATA[<p>Micro-frontend is not a new concept, it is simply a rebranded idea which is now a new buzz word. In this blog post, we'll talk about what it is and why it is here, but more importantly, I hope to illustrate that people shouldn't shy away from micro-frontend because they probably know a lot about the subject already.</p>]]></description><link>https://blog.hao.dev/micro-frontend-an-introduction</link><guid isPermaLink="false">https://blog.hao.dev/micro-frontend-an-introduction</guid><pubDate>Mon, 26 Aug 2019 15:55:00 GMT</pubDate><content:encoded>&lt;p&gt;Micro-frontend is not a new concept, it is simply a rebranded idea which is now a new buzz word. In this blog post, we&apos;ll talk about what it is and why it is here, but more importantly, I hope to illustrate that people shouldn&apos;t shy away from micro-frontend because they probably know a lot about the subject already.&lt;/p&gt;&lt;p&gt;There are many ways to implement micro-frontend and depending on the nature of the project some may fare better than others, but we will cover all this in another blog &lt;a href=&quot;/mirco-frontend-3-ways-to-implement-it&quot;&gt;Micro Frontend: 3 ways to implement it&lt;/a&gt;. If you are already familiar with it, feel free to skip to the next post.&lt;/p&gt;
&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;In the past few months, the phrase &quot;micro-frontend&quot; is gaining more traction by the day. After seeing how much people loved the idea &quot;micro-services&quot; in the backend community, I guess the frontend community felt a bit left out. The concept, like the name, borrows ideas from Micro-services, another buzz word that became popular a few years ago and people still use it as one of those things that can be thrown into a conversation to earn credibility. Today we&apos;ll talk about it and see what all the fuss is about.&lt;/p&gt;
&lt;p&gt;Here&apos;s a quick summary of micro-services if you are not familiar with it:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The central idea behind microservices is that some types of applications become easier to build and maintain when they are broken down into smaller, composable pieces which work together. Each component is continuously developed and separately maintained, and the application is then simply the sum of its constituent components. This is in contrast to a traditional, &quot;monolithic&quot; application which is all developed all in one piece. - &lt;a href=&quot;https://opensource.com/resources/what-are-microservices&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;what are microservices&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the tech sector, people are very good at labelling something with a new name and present it as a new concept. Just see how often Apple rename existing technology to better market new products, we all roll our eyes when we see/hear it but no one can argue its effectiveness. A new name comes with a new mindset, it will get people talking about it and at the same time become confused by it.&lt;/p&gt;
&lt;h2&gt;How does it work?&lt;/h2&gt;
&lt;p&gt;Just like micro-services, this new &quot;micro-frontend&quot; concept is about breaking down your application code into multiple parts so that they can be developed, maintained and work independently. But from the user&apos;s perspective, it works and feels just like a normal application.&lt;/p&gt;
&lt;p&gt;Here&apos;s a diagram illustrating what a monolith approach looks like.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3aKHccp3MPGzW7wlBbzL9g/41ad4091951369a3b9b5176b9545798d/SmartSelect_20190826-164646_Samsung_Notes.jpg&quot; alt=&quot;a diagram of monolith approach&quot;&gt;&lt;/p&gt;
&lt;p&gt;As you can see it the inner workings of the application is not straightforward by any means. In fact, it will probably take some time to understand what each part do and why they are there. Most likely they are the result of poor planning or specific business logic governing something, or both.&lt;/p&gt;
&lt;p&gt;Now let&apos;s take a look at what a micro-frontend approach looks like.  &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3pI8PRkgsTvmHpuLZWQmwy/e96880ed717167bf9cc15e8e6fae5f90/SmartSelect_20190826-164659_Samsung_Notes.jpg&quot; alt=&quot;a diagram of micro-frontend approach&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course, this is oversimplified for demonstration purposes. But the idea is that, once we split a big app into smaller ones everything becomes exponentially easier to understand. This means we can implement new features faster, fix bugs easier, and etc. Of course, there are some drawbacks with micro-frontend too which we&apos;ll discuss another time.&lt;/p&gt;
&lt;p&gt;I won&apos;t go any further with the &quot;how&quot; part here as that will be covered by the blog post mentioned above.&lt;/p&gt;
&lt;h2&gt;No, it is not a new concept&lt;/h2&gt;
&lt;p&gt;Wow, this is amazing, right? Well, not really.&lt;/p&gt;
&lt;p&gt;I think most of us are already using micro-frontend one way or another. It is pretty unlikely to see a monoliths approach that contains all of the frontend logic in one place. Businesses often split up parts of the website such as blogs, forums and even parts of the main app to help with the separation of concerns or simply to divide work between all the development teams. In many cases, it often means the tech-stack is slightly different too (which may or may not be a pain point). Out of the client projects I worked on in the past few years, I have seen a trend of people starting to split up big apps into individual Single Page Applications (SPAs) without knowing they are doing micro-frontend. Teams have been going down the right path due to other new concepts in the industry such as componentisation and etc, in pursuit of a more maintainable codebase. The reason components are so popular is because it benefits both the business and developers. Componentised apps are easier to develop and maintain, due to isolation and separation of concern it offers. Thus it also makes it easier for business to divide work among teams or team members. I think micro-frontend will become popular for the same reasons.&lt;/p&gt;
&lt;p&gt;If it is not a new concept, then why is it rising in popularity now?&lt;/p&gt;
&lt;p&gt;The problems we are trying to solve with &quot;micro-frontend&quot; aren&apos;t all that new under the surface. The only reason why this is getting more popular is due to the overused SPA in projects, more specifically, projects that implement monolithic SPA. Some large and complex websites are cramped into single SPAs are pretty extreme examples, but we all end up doing something similar sooner or later. It could be lack of time to split code up when under pressure from the business. This leads to over-complicated logic for handling user journeys, possible app states, routing, etc. Anyone who&apos;s worked on a large React-Redux app should know how complicated the state gets, with all kinds of Actions and Events triggering from different components. The store could also get too bloated and become harder to maintain after some point. Here&apos;s a good article on the issues with SPA if you want more information: &lt;a href=&quot;https://gomakethings.com/the-problem-with-single-page-apps&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;problem with single page apps&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Hopefully, now I&apos;ve convinced you that you are not looking at a new concept whcih will take a lot of effort to learn. The new &quot;micro-frontend&quot; is just here to remind us to be logical when it comes to designing and architecting new web apps. If we know a project is going to be complex and we want to use the SPA approach, then we probably should break it down before it gets too big and scary.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Self Reflection: How To Run Coding Sessions As A Volunteer]]></title><description><![CDATA[<p>After almost 4 months of volunteering, I've learned that running a weekly session for one volunteer is too much. Also don't try to teach big topics that span over a few sessions, because people will drop in and out. They will struggle after missing sessions and either stop attending or come in feeling very lost the next time. People love coding games and challenges instead of tasks with vague requirements like "make a website that uses HTML form". Going forward I'm going to try smaller bite-sized sessions so people can drop in and out more freely.</p>]]></description><link>https://blog.hao.dev/self-reflection-how-to-run-coding-sessions-as-a-volunteer</link><guid isPermaLink="false">https://blog.hao.dev/self-reflection-how-to-run-coding-sessions-as-a-volunteer</guid><pubDate>Sat, 24 Aug 2019 13:48:00 GMT</pubDate><content:encoded>&lt;p&gt;After almost 4 months of volunteering, I&apos;ve learned that running a weekly session for one volunteer is too much. Also don&apos;t try to teach big topics that span over a few sessions, because people will drop in and out. They will struggle after missing sessions and either stop attending or come in feeling very lost the next time. People love coding games and challenges instead of tasks with vague requirements like &quot;make a website that uses HTML form&quot;. Going forward I&apos;m going to try smaller bite-sized sessions so people can drop in and out more freely.&lt;/p&gt;&lt;h2&gt;Mistake 1: Weekly session is too much for me&lt;/h2&gt;
&lt;p&gt;I didn&apos;t think it through properly when I signed up and agreed to weekly sessions. I thought 1 hour (if we overrun then 1.5 hours) a week is not a big deal, I am sure I can make up time for that. Oh man, was I wrong! I forgot to take into consideration the time for planning each session, and manage the communication side of things as well.&lt;/p&gt;
&lt;p&gt;What ended up happening was I&apos;d spend 2+ hours on preparing the slides for each session, creating exercises and solutions too (because not everyone can attend every week so they need good material to keep up). I&apos;d then send out slides before the session (due to some people prefers to print out the slides for the session). I&apos;d also remind people about these sessions and answer any questions. Sometimes the session will overrun and end up being close to 2 hours instead of the 1 hour I initially planned. I don&apos;t mean to blame anyone, these are realistic expectations, I just didn&apos;t consider them at the start. Anyhow, I was spending close to 5 hours each week. This was eating up quite a lot of my free time, especially given that I have a 9 am - 5:30 pm day job Monday to Friday.&lt;/p&gt;
&lt;p&gt;If anyone is thinking of volunteering, as I&apos;ve mentioned in my &lt;a href=&quot;/volunteer-to-teach-people-to-code&quot;&gt;volunteering to teach people to code&lt;/a&gt; post I think it is a great thing to do. I strongly recommend it, not only because you are doing something to help other people in your community but also it will help you in so many ways too. My advice here is to try to give yourself some breather room by not committing to do it weekly. I think participating bi-weekly would be okay, but maybe monthly would be more sustainable. Of course, it very much depends on what it is that you do. If all you have to do is turn up on the day and have no other responsibilities then go for weekly if you can make the time. But since I am a one-man team, weekly is too challenging.&lt;/p&gt;
&lt;h2&gt;Mistake 2: Don&apos;t aim for the sky&lt;/h2&gt;
&lt;p&gt;Another mistake I made was to plan out 4 months worth of session and set out a big goal &quot;to teach people with no coding experience the basics web development&quot;. This means teaching them about HTML, CSS and JavaScript, so they could make a simple web site at the end. I think for a small number of attendees, they managed to reach the original goal. But for most people, it wasn&apos;t the case.&lt;/p&gt;
&lt;p&gt;Smaller and independent sessions are much better for this kind of free sessions. It sounds weird to say, but because it is free people feel less strongly about attending each week because it is free. If they paid for the course then they would push themselves more to attend I think. The lesson learned here is: don&apos;t expect people to turn up each week. It is probably not their fault, things come up sometimes. It probably also didn&apos;t help that I was running the sessions on Wed evenings.&lt;/p&gt;
&lt;p&gt;Going forward, I&apos;m going to plan bite-sized sessions with more realistic learning goals. My current plan is to only focus on teaching people JavaScript and show them how coding/programming works instead of teaching them about the whole web. This will allow people to drop in and out of these sessions. If they miss a few, it is no big deal they can still follow and learn. Whereas the old format means if people miss one session, it is very hard to follow the next session. This often means they&apos;d drop out completely and stop attending future sessions. For those who are more motivated, they would struggle a lot more.&lt;/p&gt;
&lt;h2&gt;Mistake 3: Specific challenges are better&lt;/h2&gt;
&lt;p&gt;It shouldn&apos;t be a surprise really, but people loved the &lt;a href=&quot;https://flexboxfroggy.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Flexbox Froggy&lt;/a&gt; exercise. I was so amazed at how many people managed to finish all 24 levels! I just want to remind you these are people who have very little coding experience and this is the second session on CSS. Everyone needed a little help, but I was still so happy almost everyone got through it all in less than 2 hours that the session after I bought snacks for everyone to reward them.&lt;/p&gt;
&lt;p&gt;In comparison, the exercises I created myself often would confuse people or just see a much lower level of engagement in general. I half expected it, because it takes time to create a good exercise and I was already spending nearly 5 hours a week at this point. So, unfortunately, the quality of the exercises wasn&apos;t as high or as fun as something like &lt;a href=&quot;https://flexboxfroggy.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Flexbox Froggy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the future, I will use one of the many code challenge websites (probably &lt;a href=&quot;https://www.codewars.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;codewars&lt;/a&gt;) and in each session, we will do some of the challenges together. Then people can go home and do some in their spare time if they like. It is gamified, so people will be more engaged for sure.&lt;/p&gt;
&lt;h2&gt;Don&apos;t let this put you off&lt;/h2&gt;
&lt;p&gt;If you are thinking about volunteering, please don&apos;t let this put you off! This post is a personal reflection and how I want to improve the sessions. It is still an amazing experience for me and I would strongly recommend it! If that is not enough to convince you, there are the added benefits of something that will give you &quot;moral high-ground&quot; to talk about in interviews :D&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Visual Studio Code: the most important 12 shortcuts to get started with]]></title><description><![CDATA[<p>New to Visual Studio Code (VS Code)? This post will walk you through the most useful and commonly used VS code shortcuts. Some of them might take some getting used to, but once you are comfortable using them instead of clicking around with a mouse you should see your productivity skyrocket!</p>]]></description><link>https://blog.hao.dev/visual-studio-code-the-most-important-12-shortcuts-to-get-started-with</link><guid isPermaLink="false">https://blog.hao.dev/visual-studio-code-the-most-important-12-shortcuts-to-get-started-with</guid><pubDate>Fri, 23 Aug 2019 10:28:00 GMT</pubDate><content:encoded>&lt;p&gt;New to Visual Studio Code (VS Code)? This post will walk you through the most useful and commonly used VS code shortcuts. Some of them might take some getting used to, but once you are comfortable using them instead of clicking around with a mouse you should see your productivity skyrocket!&lt;/p&gt;&lt;p&gt;Here&apos;s a quick summary of all the commands for anyone who are already familiar with VS Code.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + shift + P&lt;/code&gt; - to open up command palette&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + P&lt;/code&gt; - search for a file in workspace&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + T&lt;/code&gt; - search for a method in workspace&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ctrl + tab&lt;/code&gt; - switch between opened tabs&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + option + left-arrow&lt;/code&gt; &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;cmd + option + right-arrow&lt;/code&gt; - go left or right of currently opened tabs&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;ctrl + `&lt;/code&gt; - toggle integrated terminal&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + B&lt;/code&gt; - toggle &lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; - select the next occurrence of the currently highlighted text (multi-select)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + shift + L&lt;/code&gt; - select all occurrences of currently highlighted text (multi-select)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + option + up-arrow&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cmd + option + down-arrow&lt;/code&gt; will add cursor above/below (multi-select)&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;option + up-arrow&lt;/code&gt; &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;option + down-arrow&lt;/code&gt; will move the line up or down respectively&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;option + shift + up-arrow&lt;/code&gt; &amp;#x26; &lt;code class=&quot;language-text&quot;&gt;option + shift + down-arrow&lt;/code&gt; will duplicate the current line and insert it at line above or below&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you would want to learn the why behind each of the shortcuts, feel free to keep reading.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + shift + P&lt;/code&gt; to open command palette&lt;/h2&gt;
&lt;p&gt;If you don&apos;t know already, command palette is built into many software nowadays. The idea of command palette is that it gives the user one central place that allows many possible actions. E.g. if I want to open a file, open up settings, change my theme, switch project, open new workspace you name it. If there is something you can change in VS Code, you can find it or get to it via the command palette.&lt;/p&gt;
&lt;p&gt;And no, it is not just geeky developer software and tools. Mac&apos;s spotlight search is a pretty good example of this, but I personally use &lt;a href=&quot;https://www.alfredapp.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Alfred&lt;/a&gt; which is an enhanced version of it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/75svQQoC9uQNTBWiA4T0Lb/9bbd06cfeed20ae9ca3294e6a20097bc/Screenshot_2019-08-22_at_22.34.16.png&quot; alt=&quot;Screenshot 2019-08-22 at 22.34.16&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + P&lt;/code&gt; to search for a file&lt;/h2&gt;
&lt;p&gt;In large projects, in order to keep things clean, we often use very nested folder structures. This makes it a nightmare trying to find the file we are looking for. Personally, I think the only time you are allowed to use the &lt;strong&gt;Explorer&lt;/strong&gt; (the files size menu) is when you have no idea what the file is called but remember where it is located. VS Code does a great job at indexing everything, which means file searches are fast and with vague lookups, you don&apos;t even need to enter the right filename to get it to show in the results.&lt;/p&gt;
&lt;p&gt;Here&apos;s a screenshot of me butcher the word &quot;reducer&quot; and it still managed to find it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/o6a89UFouwoOoPkqFVRdh/389304392aa132b6aed64cfa0f263242/Screenshot_2019-08-22_at_22.44.00.png&quot; alt=&quot;Screenshot 2019-08-22 at 22.44.00&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;ctrl + tab&lt;/code&gt; switch between opened tabs&lt;/h2&gt;
&lt;p&gt;When coding I regularly open 3 - 5 files then constantly switch before them, in a way this is good because it means the codebase is more modular. Although it can be quite painful because you got to keep clicking on different tabs. &lt;code class=&quot;language-text&quot;&gt;ctrl + tab&lt;/code&gt; will let you cycle through all of your tabs. If that is not enough &lt;code class=&quot;language-text&quot;&gt;ctrl + shift + tab&lt;/code&gt; will cycle through them in the opposite direction, in case you missed a file this way you don&apos;t need to loop through everything again.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4a7TBvHY7BVPgyqUVnTT8H/68fb17f86a33ccb34260c99450901fcb/Screenshot_2019-08-22_at_23.15.13.png&quot; alt=&quot;Screenshot 2019-08-22 at 23.15.13&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + option + left-arrow&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;cmd + option + right-arrow&lt;/code&gt; switch to the left or right tab from current tab&lt;/h2&gt;
&lt;p&gt;Just like &lt;code class=&quot;language-text&quot;&gt;ctrl + tab&lt;/code&gt; this is another way to switch between opened tabs. This might be more easy to visualise and remember since it uses the arrow keys.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;ctrl + `&lt;/code&gt; toggle integrated terminal&lt;/h2&gt;
&lt;p&gt;Are you the type of person who uses an external terminal when you are coding in VS Code? Can I suggest that you give the integrated terminal a try? Rather than digging through the many opened apps trying to find the terminal every-so-often when you need it, simply hit `ctrl + `` to show and hide the integrated terminal. The integrated terminal can do everything your normal terminal does. Just as icy on the cake, why not try out ZSH shell which is a much better-improved Bash shell alternative. You can install it from &lt;a href=&quot;https://ohmyz.sh&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt; or if you are on latest Mac OS beta your machine already has it.&lt;/p&gt;
&lt;p&gt;You can read more about how to configure it on my other post &lt;a href=&quot;/use-zsh-shell-as-integrated-terminal-in-visual-studio-code&quot;&gt;Use ZSH shell as an integrated terminal in Visual Studio Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6EYvgyi34AalMFoLt3oxTF/67c9f94578bbf26906abca984c589d47/Screenshot_2019-08-22_at_23.25.03.png&quot; alt=&quot;Screenshot 2019-08-22 at 23.25.03&quot;&gt;&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + B&lt;/code&gt; toggle side menu&lt;/h2&gt;
&lt;p&gt;I only develop on a laptop, which has limited screen space. This means when I need to open two tabs side-by-side, I still can&apos;t see much of either tab. I normally keep the side menu hidden and bring it up when I need something e.g. explorer, source control and etc. Rather than clicking on specific icons to show hide side menu, this shortcut is a much faster way of making room for the more important stuff on the screen.&lt;/p&gt;
&lt;p&gt;There are even shortcuts to switch between the different side menu interfaces e.g. go from explorer to source control. But I&apos;d recommend nailing the shortcuts I&apos;m suggesting here first, otherwise, you might be taking on too much at once.&lt;/p&gt;
&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6JkFRpDO9PvyHpVdmJQJqz/6ba32e83087f6ada7c91825a25dc3757/Screenshot_2019-08-22_at_23.32.52.png&quot; alt=&quot;side menu&quot; width=&quot;320&quot; /&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; select the next occurrence of highlighted text&lt;/h2&gt;
&lt;p&gt;This is a very handy multi-select tool, personally, I use it daily. Imagine you have the following snippet of code, and you would like to rename all of the &lt;code class=&quot;language-text&quot;&gt;matches&lt;/code&gt; variable to &lt;code class=&quot;language-text&quot;&gt;foundMatches&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; matches &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;matchPattern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; pattern&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
 
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;matches&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// for each match, get the parsed cases&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; matches&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; dateTimeObj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;convertMatchToDateObj&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;match&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;parseMatches&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; match&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; dateTimeObj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Normally this will involve you changing the first one then copy and paste it over the other occurrences. Too slow!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/26FDkTGG3yrRuSH9478lxb/13b845095141e127b0b7011c3a00174e/Aug-23-2019_01-36-24.gif&quot; alt=&quot;Now imagine, you highlight &amp;#x60;matches&amp;#x60; and then hit &amp;#x60;cmd + D&amp;#x60; twice, you&amp;#x27;ll now have all three occurrences of &amp;#x60;matches&amp;#x60; selected. If you start changing one, then all of them will change at the same time&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is super helpful when refactoring code, or when you just decided you named something poorly. The other use case if when I&apos;m trying to understand code other people written. For example, I can just highlight the function name then hit &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; this will take me to the next place the function is used in the same file. I could just keep hitting &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; until it cycled through all of the occurrences in the file, then it&apos;ll just stop.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + shift + L&lt;/code&gt; select all occurrences of highlighted text&lt;/h2&gt;
&lt;p&gt;This is similar to &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt;, but it will select all of the occurrences of the highlighted text in the same file. This saves you time from hitting &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; multiple times if the text occurs multiples times. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/aJESfzW9NrbH2hAP9Dqyz/860c1e78b8480c321f36cf23c96783fd/Aug-23-2019_01-37-55.gif&quot; alt=&quot;cmd shift l multi-select&quot;&gt;&lt;/p&gt;
&lt;p&gt;But notice how it also selected &lt;code class=&quot;language-text&quot;&gt;parseMatches&lt;/code&gt; since it contained &lt;code class=&quot;language-text&quot;&gt;match&lt;/code&gt;, it is faster but just be careful you are not accidentally changing another similarly named variable.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;cmd + option + up-arrow&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;cmd + option + down-arrow&lt;/code&gt; will add cursor above/below&lt;/h2&gt;
&lt;p&gt;To continue on from the last two multi-select shortcuts, these two are also very useful when it comes to editing multiple lines at the same time. Instead of modifying the highlighted text that occurs multiple times in the same file, these shortcuts will insert another cursor at either the line above or below. When you start editing one line, the other cursor will also perform the same actions.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example where this shortcut might be useful.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; var1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar4&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; var2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar5&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; var3 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;address&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;bar6&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Say I want to swap the value of &lt;strong&gt;name&lt;/strong&gt; and &lt;strong&gt;address&lt;/strong&gt;, this will not be easy using the other multi-select commands. But with &lt;code class=&quot;language-text&quot;&gt;cmd + option + up-arrow&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;cmd + option + down-arrow&lt;/code&gt; it will be a piece of cake.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1gABKwx72KUs4YdYCIhvKN/923a997f83b2dc1026360336c060cad2/Aug-23-2019_01-38-56.gif&quot; alt=&quot;cmd option up-arrow or down-arrow multi-select&quot;&gt;&lt;/p&gt;
&lt;p&gt;PS: if the line context doesn&apos;t line up very well, you can also try to press and hold &lt;code class=&quot;language-text&quot;&gt;option&lt;/code&gt; and click to insert cursors.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;option + up-arrow&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;option + down-arrow&lt;/code&gt;  move current line up or down&lt;/h2&gt;
&lt;p&gt;I often see people copy or cut a line of code and then past it a few lines away from its original position. These shortcuts can achieve the same results in much fewer steps and time.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7MlyxDwJnNCajpXwgnoeVX/fdaa9e0b6110327e0a03a1846fee9d85/Aug-23-2019_01-41-45.gif&quot; alt=&quot;option up-arrow or down-arrow to move code&quot;&gt;&lt;/p&gt;
&lt;p&gt;In the above screen recording, I have moved the first time to the bottom and then back up.&lt;/p&gt;
&lt;h2&gt;&lt;code class=&quot;language-text&quot;&gt;option + shift + up-arrow&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;option + shift + down-arrow&lt;/code&gt; duplicate current line&lt;/h2&gt;
&lt;p&gt;These shortcuts will duplicate the current line and insert it at the line above or below. There are many ways to make the best out of these shortcuts during your development workflow. I find it most useful when I&apos;m playing around with code trying out a different solution.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of me commenting out the original line of code and replacing it with something slightly different. The benefit of this is that I can quickly put back the original line if I wanted to try a different approach from the same starting point.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4uxLYWUV88OlDnBwnEzfcv/8dc63ba7844c8debd75a5d5ccecc2017/Aug-23-2019_01-41-59.gif&quot; alt=&quot;option shift up-arrow or down-arrow can duplicate line&quot;&gt;&lt;/p&gt;
&lt;h2&gt;What should you care?&lt;/h2&gt;
&lt;p&gt;VS Code is the most popular Text Editor in web development right now. As you are reading this VS Code is expanding to other languages, recently there&apos;s been a lot of support for Java added. There are also many web applications which have online editors (&lt;a href=&quot;https://codesandbox.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Code Sandbox&lt;/a&gt;, &lt;a href=&quot;https://stackblitz.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Stackblitz&lt;/a&gt; and many more) that are based on VS Code, so learning it now and be good at it means this stone will hit many birds! Yes, they use the same shortcut as VS Code.&lt;/p&gt;
&lt;p&gt;Just to finish off, learning these shortcuts will not only make you look like a god when pair programming or coding in front of other people. It honestly will improve your productivity by a mile. I am so used to them in fact, I can&apos;t tell you how many times I tried &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; on Chrome browser when I&apos;m searching for something and accidentally bookmarked that page instead (who knew &lt;code class=&quot;language-text&quot;&gt;cmd + D&lt;/code&gt; is a shortcut for bookmarking on Chrome!).&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Disclaimer: by no means are these the only VS Code shortcuts worth learning, they are only my suggestions. I use these shortcuts daily for a few years back when I was using Sublime Text and Atom.&lt;/p&gt;
&lt;p&gt;Note: these shortcuts are not Mac-specific, it is just that I don&apos;t develop on Window or Linux enough to list them all out easily. You can find the above shortcuts for your platform in VS Code shortcut settings.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Trying out new blogging format]]></title><description><![CDATA[<p>Going forward I am updating how blog posts are structured. My thinking is that it will help people to consume the content and makes it a fun challenge for me. So each blog post will have summary as its first paragraph, then go on to the main body of the content. If possible, I will keep the most relevant and important stuff at the top and the least at the bottom of the page. Hope everyone (the very few people who visit my blog) enjoys the new change.</p>]]></description><link>https://blog.hao.dev/trying-out-new-blogging-format</link><guid isPermaLink="false">https://blog.hao.dev/trying-out-new-blogging-format</guid><pubDate>Thu, 22 Aug 2019 21:13:00 GMT</pubDate><content:encoded>&lt;p&gt;Going forward I am updating how blog posts are structured. My thinking is that it will help people to consume the content and makes it a fun challenge for me. So each blog post will have summary as its first paragraph, then go on to the main body of the content. If possible, I will keep the most relevant and important stuff at the top and the least at the bottom of the page. Hope everyone (the very few people who visit my blog) enjoys the new change.&lt;/p&gt;&lt;p&gt;Here&apos;s a more visual explanation:&lt;/p&gt;
&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4avJM13dTOm06zgASjuQVI/b55b81c9a52283054050ba3bc634a248/Screenshot_2019-08-22_at_22.15.12.png&quot; alt=&quot;A sketch of what I explained above&quot; width=&quot;400&quot; /&gt;
&lt;h2&gt;An interesting background story&lt;/h2&gt;
&lt;p&gt;I recently watched a video on YouTube which inspired what I&apos;m going to try out next and this post. I am currently looking to get a new iPad and I did what most people would do - search my question on YouTube. I was expecting the same old unboxing and reviewing type of videos from YouTubers whom I follow already because they tend to be big names on the platform. I&apos;m very surprised to have found this video &lt;a href=&quot;https://www.youtube.com/watch?v=xFX-lyLU2X8&amp;#x26;t=4s&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Which iPad should you buy (2019) - Analysis of iPad Pro vs iPad Air vs iPad Mini vs iPad&lt;/a&gt; by a YouTuber called Ali Abdaal. I was quite impressed with how he layout his video content.&lt;/p&gt;
&lt;p&gt;His video structure:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Quick intro and summary of the video&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;li&gt;The main content of the video&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Compared to most (if not all) other similar videos, the content is backwards. But I found the video very refreshing. I can&apos;t remember all the time I watched half or most of a video only to find I knew most of the stuff anyway and got nothing out of it. The fast the conclusion goes first means I can check what is interesting then proceed if there is something I&apos;d like to learn more about. Also, the YouTuber even added timestamps to allow people to jump to the different sections without wasting their time on things they weren&apos;t interested in.&lt;/p&gt;
&lt;p&gt;I want to do this with my blogs going forward. I&apos;m very much aware that I often give some background that might not interest most people, but it just feels odd and headless if I don&apos;t give an introduction first. Especially given the first paragraph of my blog posts are taken to show on the home page. It would be pretty useful for both people visiting my blog and myself (when I&apos;m trying to remember what I wrote about) if everything can be summarised into that one paragraph.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I hope this format makes sense and ends up improving the content layout a bit. What very few readers I have, it would be good if I can get some feedback on this. Even if you are just a random stranger who just stumbled on to my blog, feel free to leave a line in the comments :)&lt;/p&gt;
&lt;p&gt;I find Ali (the YouTuber mentioned above) quite inspiring, strongly recommend his channel!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My React Hook Notes]]></title><description><![CDATA[<p>I have been doing a lot of reading on React Hook, there's quite the hype for it. The more I read, the more I understood why. It will breakdown existing complex app logic into simpler, flatter architecture. There were some key points I wrote down as part of my reading, which I think will be very helpful later to refresh my memories and decided to upload it on here for others to see as well. It is a digested version of the reading materials I listed in the source section below.</p>]]></description><link>https://blog.hao.dev/my-react-hook-notes</link><guid isPermaLink="false">https://blog.hao.dev/my-react-hook-notes</guid><pubDate>Thu, 08 Aug 2019 18:10:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been doing a lot of reading on React Hook, there&apos;s quite the hype for it. The more I read, the more I understood why. It will breakdown existing complex app logic into simpler, flatter architecture. There were some key points I wrote down as part of my reading, which I think will be very helpful later to refresh my memories and decided to upload it on here for others to see as well. It is a digested version of the reading materials I listed in the source section below.&lt;/p&gt;&lt;blockquote&gt;
&lt;p&gt;Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;No breaking changes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Completely opt-in.&lt;/strong&gt; You can try Hooks in a few components without rewriting any existing code. But you don’t have to learn or use Hooks right now if you don’t want to.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100% backwards-compatible.&lt;/strong&gt; Hooks don’t contain any breaking changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Available now.&lt;/strong&gt; Hooks are now available with the release of v16.8.0.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No plan of removing classes&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hooks builds on top of other React concepts&lt;/strong&gt; - props, state, context, refs, and lifecycle&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Must remember&lt;/h2&gt;
&lt;p&gt;Hooks are JavaScript functions, but they impose two additional rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only call Hooks &lt;strong&gt;at the top level&lt;/strong&gt;. Don’t call Hooks inside loops, conditions, or nested functions.&lt;/li&gt;
&lt;li&gt;Only call Hooks &lt;strong&gt;from React function components&lt;/strong&gt;. Don’t call Hooks from regular JavaScript functions. (There is just one other valid place to call Hooks — your custom Hooks. We’ll learn about them in a moment.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Use the &lt;a href=&quot;https://www.npmjs.com/package/eslint-plugin-react-hooks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;linter plugin&lt;/a&gt; to enforce these rules automatically.&lt;/p&gt;
&lt;h2&gt;React Hook V.S. Traditional React Class&lt;/h2&gt;
&lt;p&gt;There are many reasons to use Hook, but the most important might to reduce complexity for both machine and people.&lt;/p&gt;
&lt;h3&gt;People&lt;/h3&gt;
&lt;p&gt;Reuse stateful logic between components in React has always been a goal and a challenge. We can achieve it with &lt;strong&gt;render props&lt;/strong&gt; and &lt;strong&gt;higher-order components&lt;/strong&gt;. But these patterns require you to restructure your components when you use them, which can be cumbersome and make code harder to follow. If you look at a typical React application in React DevTools, you will likely find a “wrapper hell” of components surrounded by layers of providers, consumers, higher-order components, render props, and other abstractions.&lt;/p&gt;
&lt;h3&gt;Machines&lt;/h3&gt;
&lt;p&gt;Class components can encourage unintentional patterns that make these optimisations fall back to a slower path. Classes present issues for today’s tools, too. For example, classes don’t minify very well, and they make hot reloading flaky and unreliable.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Component with Hook&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Declare a new state variable, which we&apos;ll call &quot;count&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCount&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;You clicked &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; times&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        Click me
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// React Class Component&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Example&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;You clicked &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; times&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;setState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;count&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
          Click me
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Hook APIs&lt;/h2&gt;
&lt;h3&gt;useState&lt;/h3&gt;
&lt;p&gt;Call it inside a function component to add some local state to it. React will preserve this state between re-renders.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// Declare a new state variable, which we&apos;ll call &quot;count&quot;&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setCount&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;You clicked &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;count&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; times&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;button onClick&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setCount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;count &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        Click me
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;button&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;useEffect&lt;/h3&gt;
&lt;p&gt;You’ve likely performed data fetching, subscriptions, or manually changing the DOM from React components before. We call these operations “side effects” (or “effects” for short) because they can affect other components and can’t be done during rendering.&lt;/p&gt;
&lt;p&gt;This hook adds the ability to perform side effects from a function component. It serves the same purpose as componentDidMount, componentDidUpdate, and componentWillUnmount in React classes, but unified into a single API.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useEffect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isOnline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setIsOnline&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleStatusChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOnline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ChatAPI&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribeToFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleStatusChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ChatAPI&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribeFromFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleStatusChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOnline &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Loading...&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Online&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Offline&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;ChatAPI.subscribeToFriendStatus&lt;/code&gt; line is the effect that subscribes to a friend’s online status.&lt;/p&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;return&lt;/code&gt; inside &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt;  would clean up and unsubscribe from our ChatAPI when the component unmount, as well as before re-running the effect due to a subsequent render.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;What does &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; do?&lt;/strong&gt; By using this Hook, you tell React that your component needs to do something after render. React will remember the function you passed (we’ll refer to it as our “effect”), and call it later after performing the DOM updates. In this effect, we set the document title, but we could also perform data fetching or call some other imperative API.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Why is &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; called inside a component?&lt;/strong&gt; Placing &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; inside the component lets us access the &lt;code class=&quot;language-text&quot;&gt;count&lt;/code&gt; state variable (or any props) right from the effect. We don’t need a special API to read it — it’s already in the function scope. Hooks embrace JavaScript closures and avoid introducing React-specific APIs where JavaScript already provides a solution.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Does &lt;code class=&quot;language-text&quot;&gt;useEffect&lt;/code&gt; run after every render?&lt;/strong&gt; Yes! By default, it runs both after the first render &lt;em&gt;and&lt;/em&gt; after every update. (We will later talk about &lt;a href=&quot;https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;how to customize this&lt;/a&gt;.) Instead of thinking in terms of “mounting” and “updating”, you might find it easier to think that effects happen “after render”. React guarantees the DOM has been updated by the time it runs the effects.&lt;/p&gt;
&lt;h4&gt;Other Tips&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Use Multiple Effects to Separate Concerns&lt;/li&gt;
&lt;li&gt;Optimising Performance by Skipping Effects&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;useContext&lt;/h3&gt;
&lt;p&gt;Accepts a context object (the value returned from &lt;code class=&quot;language-text&quot;&gt;React.createContext&lt;/code&gt;) and returns the current context value for that context. The current context value is determined by the &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt;prop of the nearest &lt;code class=&quot;language-text&quot;&gt;&amp;lt;MyContext.Provider&gt;&lt;/code&gt; above the calling component in the tree.&lt;/p&gt;
&lt;p&gt;When the nearest &lt;code class=&quot;language-text&quot;&gt;&amp;lt;MyContext.Provider&gt;&lt;/code&gt; above the component updates, this Hook will trigger a rerender with the latest context &lt;code class=&quot;language-text&quot;&gt;value&lt;/code&gt; passed to that &lt;code class=&quot;language-text&quot;&gt;MyContext&lt;/code&gt; provider.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;const value = useContext(MyContext);&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;A component calling useContext will always re-render when the context value changes. If re-rendering the component is expensive, you can optimise it by using memoization.&lt;/p&gt;
&lt;p&gt;Tip: Don’t forget that the argument to useContext must be the context object itself.&lt;/p&gt;
&lt;h3&gt;Other Hooks&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usereducer&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useReducer&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usecallback&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useCallback&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usememo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useMemo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#useref&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useRef&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#useimperativehandle&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useImperativeHandle&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#uselayouteffect&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useLayoutEffect&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-reference.html#usedebugvalue&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;useDebugValue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Custom Hooks&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Let&apos;s extract the logic into a custom Hook called useFriendStatus.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; useState&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; useEffect &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;react&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;friendID&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;isOnline&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; setIsOnline&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;handleStatusChange&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;status&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setIsOnline&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;status&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isOnline&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;useEffect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ChatAPI&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;subscribeToFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;friendID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleStatusChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      ChatAPI&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unsubscribeFromFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;friendID&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; handleStatusChange&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isOnline&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then it can be used in different components:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;isOnline &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Loading...&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Online&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Offline&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FriendListItem&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;useFriendStatus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;li style&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;color&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; isOnline &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;green&apos;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;black&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;friend&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;li&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Other Tips&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;Pass Information Between Hooks&lt;/li&gt;
&lt;li&gt;Optimising Performance by Skipping Effects&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Sources:&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://reactjs.org/docs/hooks-intro.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://reactjs.org/docs/hooks-intro.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://frontarm.com/james-k-nelson/react-hooks-intuition/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://frontarm.com/james-k-nelson/react-hooks-intuition&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://scotch.io/tutorials/5-ways-to-convert-react-class-components-to-functional-components-w-react-hooks&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://scotch.io/tutorials/5-ways-to-convert-react-class-components-to-functional-components-w-react-hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://css-tricks.com/getting-to-know-the-usereducer-react-hook/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://css-tricks.com/getting-to-know-the-usereducer-react-hook&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Write clean React code: starting with JSX]]></title><description><![CDATA[<p>React is arguably the most popular JavaScript rending library/framework right now. With its rising popularity and now dominating the job market, many similar JavaScript rendering libraries followed suit and adopted JSX into their work. In case you are new to frontend development, JSX is a JavaScript Syntax introduced as part of React (open sourced by Facebook back in 2015). Now you can find JSX in not only projects that are meant to be similar to React (e.g. <a href="!https://preactjs.com">Preact</a>) but also things like <a href="https://nerv.aotu.io" target="_blank" rel="nofollow noopener noreferrer">Nerv</a>, <a href="https://github.com/infernojs/inferno" target="_blank" rel="nofollow noopener noreferrer">infernojs</a>, <a href="https://github.com/anthonyshort/deku" target="_blank" rel="nofollow noopener noreferrer">Deku</a>, and etc.</p>]]></description><link>https://blog.hao.dev/write-clean-react-code-starting-with-jsx</link><guid isPermaLink="false">https://blog.hao.dev/write-clean-react-code-starting-with-jsx</guid><pubDate>Fri, 21 Jun 2019 20:08:00 GMT</pubDate><content:encoded>&lt;p&gt;React is arguably the most popular JavaScript rending library/framework right now. With its rising popularity and now dominating the job market, many similar JavaScript rendering libraries followed suit and adopted JSX into their work. In case you are new to frontend development, JSX is a JavaScript Syntax introduced as part of React (open sourced by Facebook back in 2015). Now you can find JSX in not only projects that are meant to be similar to React (e.g. &lt;a href=&quot;!https://preactjs.com&quot;&gt;Preact&lt;/a&gt;) but also things like &lt;a href=&quot;https://nerv.aotu.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Nerv&lt;/a&gt;, &lt;a href=&quot;https://github.com/infernojs/inferno&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;infernojs&lt;/a&gt;, &lt;a href=&quot;https://github.com/anthonyshort/deku&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Deku&lt;/a&gt;, and etc.&lt;/p&gt;&lt;p&gt;If we just look at the downloads in the past 6 months, you can see these projects have a reason to believe React and JSX is loved by the community. Even with the rise of Vue, it is still destroying the competition at the moment.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7o9G6v5SpnGRoCKyvusmjV/97ff7d608414433d83240381924348aa/Screenshot_2019-06-21_at_12.12.56.png&quot; alt=&quot;Screenshot 2019-06-21 at 12.12.56&quot;&gt;&lt;/p&gt;
&lt;p&gt;I have been using React for almost 4 years now, in projects anything between personal side projects to large scale commercial products at work. So then why am I saying we need something that&apos;s not JSX? Before we answer this question let&apos;s take a look at the syntax itself first.&lt;/p&gt;
&lt;h2&gt;The JSX syntax&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;JSX is a syntax extension to JavaScript. We recommend using it with React to describe what the UI should look like. JSX may remind you of a template language, but it comes with the full power of JavaScript. - React Documentation&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That might be a bit vague, so here&apos;s an example.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// a function component AKA dumb component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;myComponent&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; title&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; description&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; title &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;my title&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; description &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;a simple description&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; list &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;item 1&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;item 2&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;item 3&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;item 4&apos;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; listComponents &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; list&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;forEach&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;listItem&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;li&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;listItem&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;li&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;description&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;p&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;listComponents&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If you used React before then you&apos;ll know the above example is a simple Function component that displays a title, description as well as a short list. Of course, this is just a very simple example and there are other ways we could create components in React. In this example, it is simple enough that there isn&apos;t any opportunity to add in any ugly code. In the next part, we&apos;ll look at some common mistakes people make.&lt;/p&gt;
&lt;h2&gt;Common mistakes&lt;/h2&gt;
&lt;p&gt;Personally, I&apos;m not a big fan of JSX or what I prefer to call &quot;HTML-in-JS&quot;. Even if the tools support solved all the issues from a few years ago, such as weird linting errors, auto-format breaking indentation and etc. What makes it worse is the &quot;flexibility&quot; JSX offers, and unlike Vue or Angular you can seriously write some horrible code that is perfectly functional just impossible to read.&lt;/p&gt;
&lt;h3&gt;Passing Props&lt;/h3&gt;
&lt;p&gt;React is all about componentising the application, so everything is in chunk size files which means separation of logic and concern. With this componentisation, what we also have is the issue of passing data around, especially from parent component to their children. When done wrong can be a serious pain to look at.&lt;/p&gt;
&lt;p&gt;Check out this example below. This is what happens when a child component requires a lot of prop data from its parent, and all these data are passed down as individual attributes on the component tag.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt; &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;myUglyComponent
            user&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;user&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            date&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            isActive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;isActive&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            shouldDisplay&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;shouldDisplay&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            selectedDate&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;selectedDate&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            options&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;options&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            activePeriod&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;activePeriod&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            propertyOne&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;propertyOne&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            propertyTwo&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;propertyTwo&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            propertyThree&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;propertyThree&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            propertyFour&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;propertyFour&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            propertyFive&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;propertyFive&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;By the way, this is nowhere close to the worst I&apos;ve seen people do. No joke, I&apos;ve seen child component with as many as 20 props passed to them. Also, there are multiple of these children components in the same file. To me this is terrible, and we should avoid dumping so much unnecessary data into the return function. It is JSX&apos;s fault for allowing this in the first place, but we should know the difference between &quot;okay amount of attributes&quot; and &quot;too damn many to see without scrolling&quot;.&lt;/p&gt;
&lt;p&gt;Please just create a variable before the return or render and pass in as one prop value instead, see the example below.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    user&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    date&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    isActive&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    shouldDisplay&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    selectedDate&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    options&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    activePeriod&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

 &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;myUglyComponent data&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I hope everyone agrees that this is much easier to read. People might ask &quot;what about the proptype checking?&quot;, well it is no different to proptype checking than other objects passed into a component. Below is an example of proptype checking for nested objects.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; PropTypes &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prop-types&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;    

&lt;span class=&quot;token literal-property property&quot;&gt;propTypes&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;shape&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;date&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;isRequired&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;isActive&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; PropTypes&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;bool&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Conditional (ternary) operator&lt;/h2&gt;
&lt;p&gt;I love using ternaries, they are concise and to the point. But when used wrongly, they could make the code confusing to read even in plain JavaScript not even mentioning JSX. Now let&apos;s see how we can break the spirit of the next person looking at our code.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      isValidComponent
        &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentOne &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Error &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; shouldDisplay &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentTwo &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Did you find the above example difficult to understand? Probably not, again this is a simple example. But imagine a component with a few of these in the return logic. I don&apos;t think many will argue for this kind of approach. Ternaries should only be used if it doesn&apos;t sacrifice readability if anything it should improve it.&lt;/p&gt;
&lt;p&gt;Just for fun, here&apos;s something else I&apos;ve seen before, that should only exist in nightmares.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            validData
            &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
                displaytype
                    &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentOne &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
                    &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentTwo &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Error &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; shouldDisplay &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentThree &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is not even difficult to fix. Just like the part, we talked about passing props, let&apos;s move some of the logic outside of the return and then see.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderComponentOne &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;validData &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; displaytype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentOne &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderComponentTwo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;validData &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;displaytype&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentTwo &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderComponentThree &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;  shouldDisplay &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentThree &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; renderError &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;validData&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ComponentThree &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;title&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;h1&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderComponentOne &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderComponentTwo &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderComponentThree&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; renderError &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Deal with complex business logic&lt;/h2&gt;
&lt;p&gt;Often due to product requirements, we need to add in logic to handle specific cases and scenarios. Over time, this logic would build on top of each other and can become difficult to maintain.&lt;/p&gt;
&lt;p&gt;I think it is always important to remember, a component is meant to be isolated from other parts of the application. But this doesn&apos;t mean we cannot break a complex component down into multiple files.&lt;/p&gt;
&lt;h3&gt;Ultilities/Helper Files&lt;/h3&gt;
&lt;p&gt;There is nothing wrong with having multiple utilities or helper files in a project, not only do we abstract out common code. I have seen both projects maintaining a single file approach to avoid duplication of logic, but I also worked on projects where the team preferred many single logic helper files. It doesn&apos;t matter which of these approaches teams go with, the important thing here is that the logic is abstracted out from the over-complicated component.&lt;/p&gt;
&lt;p&gt;These helpers will (or should) always be just JavaScript code. This means it is super easy to unit test these functions.&lt;/p&gt;
&lt;h3&gt;High-Order Components (HOC)&lt;/h3&gt;
&lt;p&gt;We could have a component that only handles basic presentational logic e.g. render a component. Then we can create a High-Order Function (HOC) component to handle the complex product requirements. This way we have cleaner components, and also this also makes testing a lot easier with this abstraction.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Higher Order Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;hoc&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;WrappedComponent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Fragment&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;WrappedComponent &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        £&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;WrappedComponent&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Fragment&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;


&lt;span class=&quot;token comment&quot;&gt;// Component&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;Price&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;children&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;span&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Call HOC&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; ConvertedPrice &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;hoc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Price&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// App&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token function-variable function&quot;&gt;App&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;ConvertedPrice&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;17.00&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;ConvertedPrice&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;App &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;root&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;See &lt;a href=&quot;!https://css-tricks.com/what-are-higher-order-components-in-react&quot;&gt;CSS Tricks: What are HOC&lt;/a&gt; for more information.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Ways to improve list loading performance]]></title><description><![CDATA[<p>As developers lists are one of the main things we work with, for no other reason than it is such a powerful data visualisation technique.  We use it so much that it is second nature to dump a list of data on the screen. This is fine for users with good hardware devices and fast internet connection. But what about when we don't have the ideal scenario? In this article, we'll talk about some nice techniques we can use to improve user experience with lists on low-end devices or slow internet connection.</p>]]></description><link>https://blog.hao.dev/ways-to-improve-list-loading-performance</link><guid isPermaLink="false">https://blog.hao.dev/ways-to-improve-list-loading-performance</guid><pubDate>Mon, 10 Jun 2019 17:41:00 GMT</pubDate><content:encoded>&lt;p&gt;As developers lists are one of the main things we work with, for no other reason than it is such a powerful data visualisation technique.  We use it so much that it is second nature to dump a list of data on the screen. This is fine for users with good hardware devices and fast internet connection. But what about when we don&apos;t have the ideal scenario? In this article, we&apos;ll talk about some nice techniques we can use to improve user experience with lists on low-end devices or slow internet connection.&lt;/p&gt;&lt;h2&gt;Pagination&lt;/h2&gt;
&lt;p&gt;Pagination in essentially divides up a long list of results in pages, then allow the user to see a subset of the results on each page. This is probably the most common way of dividing content into more manageable chunks so that only a small amount of data is requested, sent and displayed to the user. No doubt this will result in snappier user experience. This practice is easy to implement and easy to handle long lists on web pages. This technique became popular on desktop devices and is still on many sites today when a user might want to skip some content in the middle. For example, going from page 1 to page 5.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2IiOdhhdNo3XQDcxQvWnQ8/69cab548b8e59df9b6b466872ece9457/Screenshot_2019-06-10_at_18.14.08.png&quot; alt=&quot;Screenshot 2019-06-10 at 18.14.08&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Lazy Loading&lt;/h2&gt;
&lt;p&gt;Lazy Loading is very similar to Pagination since you could achieve both with the same backend API. The difference here is how the user perceives the results and interact with it. With Lazy Loading, the user has to scroll to a particular position and then loading will be triggered and results loaded automatically. This allows the user to continue to browse without clicking on anything. Lazy Loading makes the most sense to be used in situations where the most relevant data is at the top and the user is not expected to want to skip any data in the middle. Or in some cases like twitter or facebook, these companies never want you to stop scrolling, so lazy loading will supply an endless amount of feeds as you continue to zombie-scroll.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/QwJ9Qi9ikdgdEfzybU9Fv/f1c3f276972b51744d86fa87d88174e7/Screenshot_2019-06-10_at_18.44.40.png&quot; alt=&quot;Screenshot 2019-06-10 at 18.44.40&quot;&gt;&lt;/p&gt;
&lt;p&gt;(Screenshot taken from &lt;a href=&quot;https://www.premierinn.com/gb/en/search.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.premierinn.com/gb/en/search.html&lt;/a&gt;)&lt;/p&gt;
&lt;h2&gt;Removal of content when it is outside of the viewport&lt;/h2&gt;
&lt;p&gt;Now we are getting to the optimised list rendering territory. Since on low-end devices, memory is a key limitation, by switching out items for fake ones we are holding fewer data in memory and thus reduces RAM shortages on these devices.&lt;/p&gt;
&lt;p&gt;This approach is just as it sounds when the list item is outside of the viewport then replace it with a fake item. When the user scrolls and the item come back into the item, then we&apos;ll do the opposite and replace the fake item with the real item again.&lt;/p&gt;
&lt;p&gt;It is also recommended to keep 1 or 2 extra tiles off-screen, so when the user scrolls off-screen tiles into the viewport they don&apos;t constantly see images being re-downloaded.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2C0Sqtb2ZpjScdkAyAxiWu/c73efbfdf41805a81b5b82dbbefa3777/List_optimisation__1___2_.png&quot; alt=&quot;List optimisation (1) (2)&quot;&gt;&lt;/p&gt;
&lt;h3&gt;A small case study&lt;/h3&gt;
&lt;p&gt;I am working on a project where we are developing web apps that sits inside a Cordova shell which runs on TV devices. The first thing to know about TV UI is that since these devices are designed to be used from far away, this means bigger UI and Text on the screen, therefore less on-screen content at a given time.&lt;/p&gt;
&lt;p&gt;So why is Pagination and Lazy Loading not enough on TVs? Firstly there is the hardware limitation, this kind of apps often face performance issues due to the devices have lower specifications compared to laptops or even mid to high-end mobile devices. When devices try to load a long list, without doing any garbage collection it can consume too much RAM and result in a laggy device. By garbage collecting tiles off-screen, we can effectively reduce the amount of data held in memory, and when the user scrolls the item into viewport again we can simply re-render the item on the screen again.&lt;/p&gt;
&lt;h2&gt;Pre-fetching Lazy Loading&lt;/h2&gt;
&lt;p&gt;This optimisation might be useful for users with a slower internet connection. This technique essentially keeps extra data in memory that is not rendered out, and each time the user reaches to the bottom of the page, it will render out the extra data and pre-fetch more data for another future lazy load. The extra data is acting as a buffer. The whole point of this optimisation is to deliver a scroll behaviour that seems to the user where everything is loading in lightning speed. Something that might not be an issue for most people, but might be a nice optimisation to improve people&apos;s user journey on slower internet speeds.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/GmEjmy3soitZIxxvah2x1/1b34bf8600086b21f69eda10cd41e1d5/List_Optimisation__2___1_.png&quot; alt=&quot;List Optimisation (2) (1)&quot;&gt;&lt;/p&gt;
&lt;p&gt;For example, we can have a site that initially loads 20 items, but we only show 10 of them. When the user gets to the bottom of the 1 - 10 item list, it will try to show the next 10 items (11 - 20) which we already have data for. Since we already have the data, so we just need to render it out. What the pre-fetching means is that we will load the next 10 items (21 - 30) as we are rending out the items 11 - 20 on the screen. So by the time the user scroll down to the bottom again, we already have 21 - 30 ready.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;Web app optimisation has become an interesting topic for me recently. Partly because I&apos;m working with more low-end devices at the moment where performance is a serious factor to consider. More importantly, these are interesting challenges to tackle, as it requires better design and clever implementations instead of just dump a bunch of data on the screen. I hope this blog post will inspire some more optimised sites!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to configure Cordova app to filter TV devices by default]]></title><description><![CDATA[<p>In this article, we'll be covering how to do configure Cordova to automatically filter supported devices. Thus saving you or someone else ticking and unticking hundreds if not thousands of boxes on Android app stores.</p>]]></description><link>https://blog.hao.dev/how-to-configure-cordova-app-to-filter-tv-devices-by-default</link><guid isPermaLink="false">https://blog.hao.dev/how-to-configure-cordova-app-to-filter-tv-devices-by-default</guid><pubDate>Tue, 21 May 2019 14:19:00 GMT</pubDate><content:encoded>&lt;p&gt;In this article, we&apos;ll be covering how to do configure Cordova to automatically filter supported devices. Thus saving you or someone else ticking and unticking hundreds if not thousands of boxes on Android app stores.&lt;/p&gt;&lt;p&gt;If you never used Cordova before, here&apos;s an official explanation.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Cordova wraps your HTML/JavaScript app into a native container which can access the device functions of several platforms. These functions are exposed via a unified JavaScript API, allowing you to easily write one set of code to target nearly every phone or tablet on the market today and publish to their app stores.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Basically, it creates a native application wrapper/shell for your web application. This is super neat and has many advantages, of course, here are just some of them on top of my head:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster updates - deploy changes without user installing another APK&lt;/li&gt;
&lt;li&gt;Use same tech stack as web - instead of dedicated platform teams, you could just have one team that works on all platforms since they all use the same technology&lt;/li&gt;
&lt;li&gt;Avoid weird proprietary technology - imagine trying to hire Play Station developers to create an app V.S. web developers.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No doubt this approach also comes with disadvantages too. We&apos;ll need to find ways to configure things which is very easy to do on these platforms. The issue we are taking a look today is trying to get our Android package (APK) to automatically filter the supported devices when we upload it to an app store. So far I&apos;ve only tested this on Amazon&apos;s Appstore, so I&apos;m not sure if it works 100% on Google&apos;s play store. But I&apos;m quite confident it should work fine.&lt;/p&gt;
&lt;h2&gt;Instructions&lt;/h2&gt;
&lt;p&gt;Here&apos;s the snippet that we need to insert into &lt;code class=&quot;language-text&quot;&gt;config.xml&lt;/code&gt; file, in the root level of our Cordova app.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;config-file parent=&quot;/manifest&quot; target=&quot;AndroidManifest.xml&quot;&gt;
    &amp;lt;uses-feature android:name=&quot;android.software.leanback&quot; android:required=&quot;true&quot; /&gt;
    &amp;lt;uses-feature android:name=&quot;android.hardware.touchscreen&quot; android:required=&quot;false&quot; /&gt;
&amp;lt;/config-file&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;What does is it doing and why do we need it?&lt;/p&gt;
&lt;h3&gt;config-file tag&lt;/h3&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;config-file&gt;&lt;/code&gt; tag &lt;a href=&quot;https://cordova.apache.org/docs/en/latest/plugin_ref/spec.html#config-file&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cordova official documentation&lt;/a&gt; states that it allows you to append new children to an XML document tree. The children are XML literals to be inserted in the target document, which in our case is the &lt;strong&gt;AndroidManifest.xml&lt;/strong&gt;. The parent attribute means where about it inserts into the file, thus they will all be inserted inside the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;manifest&gt;&lt;/code&gt; tag.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;?xml version=&apos;1.0&apos; encoding=&apos;utf-8&apos;?&gt;
&amp;lt;manifest ... xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
    ...
    &amp;lt;uses-feature android:name=&quot;android.software.leanback&quot; android:required=&quot;true&quot; /&gt;
    &amp;lt;uses-feature android:name=&quot;android.hardware.touchscreen&quot; android:required=&quot;false&quot; /&gt;
    ...
&amp;lt;/manifest&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Notice how the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;config-file&gt;&lt;/code&gt; tag is gone in the &lt;strong&gt;AndroidManifest.xml&lt;/strong&gt; file after the insertion.&lt;/p&gt;
&lt;h3&gt;android.software.leanback&lt;/h3&gt;
&lt;p&gt;This basically tells the app store that our app is designed for TV devices or the &quot;lean back experience&quot;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&amp;lt;uses-feature android:name=&quot;android.software.leanback&quot; android:required=&quot;true&quot; /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;Declare that your app uses the Leanback user interface required by Android TV. If you are developing an app that runs on mobile (phones, wearables, tablets, etc.) as well as Android TV, set the &lt;code class=&quot;language-text&quot;&gt;required&lt;/code&gt; attribute value to &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;. If you set the &lt;code class=&quot;language-text&quot;&gt;required&lt;/code&gt; attribute value to &lt;code class=&quot;language-text&quot;&gt;true&lt;/code&gt;, your app will run only on devices that use the Leanback UI. - &lt;a href=&quot;https://developer.android.com/training/tv/start/start#leanback-req&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Android official documentation&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;android.hardware.touchscreen&lt;/h3&gt;
&lt;p&gt;This should be quite easy to guess, basically says our app is not designed for touch input devices. Which disables those devices by default when we upload our APK.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The app uses the device&apos;s touchscreen capabilities for gestures that are more interactive than basic touch events, such as a fling. This is a superset of the &lt;code class=&quot;language-text&quot;&gt;android.hardware.faketouch&lt;/code&gt; feature. - &lt;a href=&quot;https://developer.android.com/guide/topics/manifest/uses-feature-element#touchscreen-hw-features&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Android official documentaion&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Final Product&lt;/h2&gt;
&lt;p&gt;Here is what we get at the end, of course, there are probably plenty other XML tags within &lt;code class=&quot;language-text&quot;&gt;&amp;lt;widget&gt;&lt;/code&gt; I&apos;ve left them out here just to make sure everything is easy to read.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;`&amp;lt;?xml version=&apos;1.0&apos; encoding=&apos;utf-8&apos;?&gt;
&amp;lt;widget ... xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;&gt;
    ...
    &amp;lt;platform name=&quot;android&quot;&gt;
        &amp;lt;config-file parent=&quot;/manifest&quot; target=&quot;AndroidManifest.xml&quot;&gt;
            &amp;lt;uses-feature android:name=&quot;android.software.leanback&quot; android:required=&quot;true&quot; /&gt;
            &amp;lt;uses-feature android:name=&quot;android.hardware.touchscreen&quot; android:required=&quot;false&quot; /&gt;
        &amp;lt;/config-file&gt;
    &amp;lt;/platform&gt;
    ...
&amp;lt;/widget&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Volunteer to teach people to code]]></title><description><![CDATA[<p>I volunteered a couple of weeks back and offered to teach whoever is interested to learn to code. It takes place in my local library, which fortunately for me, is very close to both where I live and work. Today, I just had my first session. This is the first time I'm doing something like this, though I'd share my experience and learnings. In case anyone is thinking of doing something similar.</p>]]></description><link>https://blog.hao.dev/volunteer-to-teach-people-to-code</link><guid isPermaLink="false">https://blog.hao.dev/volunteer-to-teach-people-to-code</guid><pubDate>Wed, 08 May 2019 20:33:00 GMT</pubDate><content:encoded>&lt;p&gt;I volunteered a couple of weeks back and offered to teach whoever is interested to learn to code. It takes place in my local library, which fortunately for me, is very close to both where I live and work. Today, I just had my first session. This is the first time I&apos;m doing something like this, though I&apos;d share my experience and learnings. In case anyone is thinking of doing something similar.&lt;/p&gt;&lt;h2&gt;How to get started?&lt;/h2&gt;
&lt;p&gt;I had this idea of setting something up and teaching people to code but never took it to the next stage. Luckily for me, someone asked on Nextdoor (a social network website for local communities), and I offered to help and volunteer.&lt;/p&gt;
&lt;p&gt;Surprisingly, getting it all set up and running were so easy from my point of view. Fiona from the local council emailed me a simple form to fill in, then offered me a number of locations for me to choose from. Other people sorted out the location and finding people who were interested. I didn&apos;t have to a thing, apart from going to the library for a quick interview and hand in my form. Before I knew it, it was ready to go!&lt;/p&gt;
&lt;p&gt;I am a bit nervous about speaking in front of a lot of people, so I was hoping for a small group. The smaller the better, even if it was just 1 person I would have been happy. Apparently, 20+ people signed up but 18 people turned up today, which is still a huge number of people in my opinion.&lt;/p&gt;
&lt;p&gt;The conclusion is that there is so much support around volunteers, it can&apos;t be easier to get involved and give something back to the community. Everyone is super friendly, emailing to see how I was feeling after today&apos;s session and etc. To be honest, I don&apos;t think I worked anywhere where I felt so cared for, haha!&lt;/p&gt;
&lt;p&gt;My advice is don&apos;t be like me, and wait for something to fall on my lap, which is not a healthy habit for many reasons. If you are interested in  volunteering just go for it, I mean what&apos;s the worst that can happen?&lt;/p&gt;
&lt;h2&gt;Learning from each other&lt;/h2&gt;
&lt;p&gt;Yes, I volunteered to help others. But I know my public speaking ability is shocking, and lack a lot of experience. So I knew there&apos;ll be many learning opportunities for me when I signed up for this. Just in the span of 40 minutes, I already noticed things that I wasn&apos;t doing very well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When we were waiting for people to come, I didn&apos;t immediately start a conversation, the room was silent and awkward. When I realised this, even before the session started, I went around the room and asked people some simple questions like &quot;why they were here&quot; and &quot;what are their coding skills level&quot; to get a conversation going.&lt;/li&gt;
&lt;li&gt;I wrote down notes, fairly detailed notes as well. But I didn&apos;t even get to look at them, because there&apos;s 18 people staring (okay, just looking) at me at all time. I completely forgot to check my laptop. Stuff I planned to ask, I forgot. Not mentioning things I didn&apos;t even think of asking, like asking people for their email address so I could contact them about the next session! But luckily I had people supporting me, they were clearly way more experience at this.&lt;/li&gt;
&lt;li&gt;At the start, I keep focusing on one side of the room, it must have been quite obvious for other people. But I only realised after 10 - 20 minutes, so for the rest of the session, I forced my self to look around a bit more and not look at any one place for too long.&lt;/li&gt;
&lt;li&gt;Going back to my public speaking point, I noticed myself repeating myself and losing track of the overall direction of the conversation. At one point I had no idea what I need to say or ask, but some BS skills came into play and just kept on going.&lt;/li&gt;
&lt;li&gt;The attendees asked a lot of good and useful questions, which not only reminded what I planned to say but also gave me an opportunity to pause and think. So I kept on asking them if they had more questions :D&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Hold your ground?&lt;/h2&gt;
&lt;p&gt;Today, I went in thinking I&apos;m going to run these sessions once every two weeks because I&apos;m worried it will suck up too much of my personal time. But after seeing people&apos;s desire to learn, I felt I should try and do more, and ended up committing to weekly sessions. I don&apos;t know if this is good or bad, I think I&apos;m happy with my decision. But if I knew I couldn&apos;t afford to do weekly sessions, then I hope in that situation I would have held my ground and stuck with what I planned.&lt;/p&gt;
&lt;h2&gt;Happiness&lt;/h2&gt;
&lt;p&gt;A final point, maybe even the most important point. I was so hyped after today&apos;s session, especially, with people coming to talk to me after asking me a question and saying thank you. Biologically, helping others makes us feel good. So if you want some more happiness, this isn&apos;t a bad way of getting some dopamine hits!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to set up Site Preview with Contentful, Gatsby and Netlify]]></title><description><![CDATA[<p>It has been a while since I wanted to get preview working for my blog. There is a lot of custom styling on my blog site, and often things don't look quite right when I publish an article for the first time. This often means it will take a few republishes before getting everything nailed. If someone were to visit my site during this stage, it wouldn't have been good user experience. So I was always curious about the Preview function on Contentful. Last night, I decided to take a stab at it after seeing this <a href="https://www.contentful.com/developers/docs/extensibility/apps/netlify" target="_blank" rel="nofollow noopener noreferrer">contentful doc</a>. It looked easy enough, but as always, I encountered some issues. After getting everything working, I thought I'd share my solution on here, maybe it'll help someone else out.</p>]]></description><link>https://blog.hao.dev/how-to-set-up-site-preview-with-contentful-gatsby-and-netlify</link><guid isPermaLink="false">https://blog.hao.dev/how-to-set-up-site-preview-with-contentful-gatsby-and-netlify</guid><pubDate>Wed, 01 May 2019 11:07:00 GMT</pubDate><content:encoded>&lt;p&gt;It has been a while since I wanted to get preview working for my blog. There is a lot of custom styling on my blog site, and often things don&apos;t look quite right when I publish an article for the first time. This often means it will take a few republishes before getting everything nailed. If someone were to visit my site during this stage, it wouldn&apos;t have been good user experience. So I was always curious about the Preview function on Contentful. Last night, I decided to take a stab at it after seeing this &lt;a href=&quot;https://www.contentful.com/developers/docs/extensibility/apps/netlify&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;contentful doc&lt;/a&gt;. It looked easy enough, but as always, I encountered some issues. After getting everything working, I thought I&apos;d share my solution on here, maybe it&apos;ll help someone else out.&lt;/p&gt;&lt;h2&gt;The complete setup flow&lt;/h2&gt;
&lt;p&gt;This article assumes that you have all the pre-requisites set up and configured already, which includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using Gatsby static site generator&lt;/li&gt;
&lt;li&gt;Have a site hosted Netlify with the build passing&lt;/li&gt;
&lt;li&gt;Content is hosted on Contentful&lt;/li&gt;
&lt;li&gt;All the normal integration between Netlify and Contentful are done, only missing the preview functionality&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;1. Install Netlify App on Contentful&lt;/h3&gt;
&lt;p&gt;Click on Apps in the Contentful menu.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2VmG4AmR9XHaoDKzmVnqMv/c6b70434fb02ac0607e5f91b0c680ec2/Screenshot_2019-05-01_at_11.24.41.png&quot; alt=&quot;Screenshot 2019-05-01 at 11.24.41&quot;&gt;&lt;/p&gt;
&lt;p&gt;Then click on Netlify in the list of supported apps. This will bring up a modal, simply click on &quot;Authorize&quot; button. If you are logged into Netlify already, then it will self-close.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5uAzehTT0mA8LW3f4jo4Q0/81ea03161dc681bc64bf0313a9117f94/Screenshot_2019-05-01_at_11.25.26.png&quot; alt=&quot;Screenshot 2019-05-01 at 11.25.26&quot;&gt;&lt;/p&gt;
&lt;h3&gt;2. Configure sites on contentful&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Use the dropdown to chose a site on Netlify you want to build. Usually, it makes sense to have two sites to build production (using the Content Delivery API) and preview (using the Content Preview API) versions of your site.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1zTKXh8yQE9ovqDXzcQljX/81567f89531647614137c19e43b30e44/Screenshot_2019-05-01_at_11.25.58.png&quot; alt=&quot;Screenshot 2019-05-01 at 11.25.58&quot;&gt;&lt;/p&gt;
&lt;p&gt;Once Netlify App has been authorised, the next step is to configure the Production and Preview sites.&lt;/p&gt;
&lt;p&gt;I recommend go to Netlify and duplicated the site again, so we have one site for Production build and another for Preview build. This is due to for Preview build, we need to use a different accessToken compared to Production build. Having two sites on Netlify means we can rely on one of them for preview builds without affecting the production site.&lt;/p&gt;
&lt;p&gt;If you haven&apos;t done it already, I also recommend moving &lt;strong&gt;accessToken&lt;/strong&gt; and other sensitive API data into &lt;strong&gt;.env&lt;/strong&gt; file and then add it to ignored file list (e.g. gitignore) so it doesn&apos;t end up in version control. &lt;/p&gt;
&lt;p&gt;The image below shows where you can find the API keys on Contentful.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4mYR1QfSNjxlLAHx9IUrHP/65dc89ca6ab34f108f6047676658daa1/Screenshot_2019-05-01_at_11.26.56.png&quot; alt=&quot;Screenshot 2019-05-01 at 11.26.56&quot;&gt;&lt;/p&gt;
&lt;h3&gt;3. Update site code&lt;/h3&gt;
&lt;p&gt;In order to build preview, we need to modify our Gatsby code a little. More specifically, the configuration file &lt;strong&gt;gatsby-config.js&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Add &lt;strong&gt;host&lt;/strong&gt; into the &lt;strong&gt;options&lt;/strong&gt; we pass into &lt;strong&gt;gatsby-source-contentful&lt;/strong&gt;. The value should be &lt;strong&gt;preview.contentful.com&lt;/strong&gt; for Preview and &lt;strong&gt;cdn.contentful.com&lt;/strong&gt; for Production.&lt;/p&gt;
&lt;p&gt;See the snippet below for how I&apos;ve done this. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// .env&lt;/span&gt;
&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_ACCESS_TOKEN&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;xxxxxxxxxxx
&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_SPACE_ID&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;xxxxx
&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_HOST&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;preview&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;contentful&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;com

&lt;span class=&quot;token comment&quot;&gt;// gatsby-config.js&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;dotenv&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;gatsby-source-contentful&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
              &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;spaceId&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_SPACE_ID&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;accessToken&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_ACCESS_TOKEN&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
                  &lt;span class=&quot;token literal-property property&quot;&gt;host&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;env&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;CONTENTFUL_HOST&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;||&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;cdn.contentful.com&apos;&lt;/span&gt;
              &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
          &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;4. Netlify Content Preview&lt;/h3&gt;
&lt;p&gt;The way to use Preview feature is by opening up a piece of content, then on the right-hand side you can click on &quot;Build&quot; to trigger a build or &quot;Open preview&quot; to see the page in action.&lt;/p&gt;
&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/22pG2acNu0pOKwDKWE4eak/270bfb43979eb3dc926d3893aa8006c8/Screenshot_2019-05-01_at_14.55.13.png&quot; alt=&quot;Build &amp; Preview&quot; width=&quot;400&quot;/&gt;
&lt;h2&gt;Issues I encountered&lt;/h2&gt;
&lt;h3&gt;Invalid Contenful spaceId/accessToken&lt;/h3&gt;
&lt;p&gt;When I try to build using the Preview API accessToken, I get the following error.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Accessing your Contentful space failed. Perhaps you&apos;re offline or the spaceId/accessToken is incorrect.
Try running setting GATSBY_CONTENTFUL_OFFLINE=true to see if we can serve from cache.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It is saying spaceId/accessToken is incorrect, but I know for sure both of these are correct because I copied from the API page on Contentful. I tried to check out any documentation on Contentful but found nothing. Surprisingly, there were very few questions asked about this online as well. Maybe because it is still quite new?&lt;/p&gt;
&lt;p&gt;It turns out the solution is exactly what I explained in step 3 in the previous section. I had to add &lt;strong&gt;host&lt;/strong&gt; into the &lt;strong&gt;options&lt;/strong&gt; we pass into &lt;strong&gt;gatsby-source-contentful&lt;/strong&gt;. It seems in order to retrieve draft documents from Contentful, you need to hit a completely different endpoint, hmmm interesting.&lt;/p&gt;
&lt;h3&gt;Failed to create a page&lt;/h3&gt;
&lt;p&gt;The second error I came across was thrown by &lt;strong&gt;gatsby-node.js&lt;/strong&gt;, complaining that it couldn&apos;t create a page because there are a lot of null values.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Your site&apos;s &quot;gatsby-node.js&quot; must set the page path when creating a page

{ path: null,
  component: &apos;/Users/hao/Documents/workspace/gatsby-blog/src/templates/blog-post.js&apos;,
  context: { slug: null } }
error See the documentation for createPage https://www.gatsbyjs.org/docs/actions/#createPage&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After I did some investigation and printed out the code that failed, I realised this error was caused by the fact I had a blank draft saved. The missing mandatory fields were causing all these error when Gatsby tries to create a page for that record.&lt;/p&gt;
&lt;p&gt;So if you encounter the same or similar errors, maybe check Contentful contents and ensure all the mandatory fields are filled out.&lt;/p&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;The benefit of the preview feature is that I don&apos;t have to trigger a production build to see what the blog post looks sitting on the site. However, the build time is still quite long, so the &quot;write something&quot; and &quot;see the changes&quot; process can still be further improved. But that&apos;s a problem for another day!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Docker Cheat sheet]]></title><description><![CDATA[<p>It seems like everyone has their own Docker Cheatsheet nowadays, and I thought I'd add one more into the mix. I've noted down some handy commands I use most often when I work on Docker-related tasks.</p>]]></description><link>https://blog.hao.dev/docker-cheat-sheet</link><guid isPermaLink="false">https://blog.hao.dev/docker-cheat-sheet</guid><pubDate>Fri, 26 Apr 2019 20:40:00 GMT</pubDate><content:encoded>&lt;p&gt;It seems like everyone has their own Docker Cheatsheet nowadays, and I thought I&apos;d add one more into the mix. I&apos;ve noted down some handy commands I use most often when I work on Docker-related tasks.&lt;/p&gt;&lt;h2&gt;Cheat sheet&lt;/h2&gt;
&lt;h3&gt;Status of docker&lt;/h3&gt;
&lt;p&gt;A quick and easy way to see an overview of docker on your machine. E.g. how many containers and images have been downloaded and their status.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker info&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Build Docker image&lt;/h3&gt;
&lt;p&gt;This is only required if we want to create a Docker image from a Dockerfile.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker build .
docker build -t newImage . // tag the image&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Run Docker container&lt;/h3&gt;
&lt;p&gt;Once we create our own Docker image or pull one from a registry, we&apos;d want to run it on our machine.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker run alpine:3.9
docker run -d alpine:3.9 // detached mode
docker run -it alpine:3.9 /bin/bash // interactive mode
docker run -v &quot;$(pwd)/src:/app&quot; alpine:3.9 // map volume
docker run -p 8080:8000 alpine:3.9 // map port&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Stop and remove all Docker containers&lt;/h3&gt;
&lt;p&gt;Wipe all containers. This is especially handy after playing around with a bunch of ideas on the development machine.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker stop $(docker ps -aq) // OR docker stop $(docker ps -a -q)
docker rm $(docker ps -aq)  // OR docker rm $(docker ps -a -q)

docker rm -f $(docker ps -aq) // doing it all in one line with force remove&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Remove all Docker images&lt;/h3&gt;
&lt;p&gt;I personally prefer to have no Docker images on my machine unless they are in use. This is a brutal but easy way to get rid of all the random images that are taking up precious storage space.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;docker rmi $(docker images -q)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Create personal email address with custom domain]]></title><description><![CDATA[<p>Since Oct 2018, roughly 6 months ago, I have decided to utilise my own custom domain and create my own personal email address with this domain. In my mind, it was always going to be some complex process, and have been avoiding it until now. But recently, I found an easy way to do this and thought I'd share it on here.</p>]]></description><link>https://blog.hao.dev/create-personal-email-address-with-custom-domain</link><guid isPermaLink="false">https://blog.hao.dev/create-personal-email-address-with-custom-domain</guid><pubDate>Mon, 22 Apr 2019 20:48:00 GMT</pubDate><content:encoded>&lt;p&gt;Since Oct 2018, roughly 6 months ago, I have decided to utilise my own custom domain and create my own personal email address with this domain. In my mind, it was always going to be some complex process, and have been avoiding it until now. But recently, I found an easy way to do this and thought I&apos;d share it on here.&lt;/p&gt;&lt;h2&gt;Why you might want to do this&lt;/h2&gt;
&lt;p&gt;Personally, I wanted a way to separate my personal and work emails. It can be quite frustrating to get LinkedIn and other job emails spamming my personal inbox. It just means I have to constantly check my phone all the time. If I had them in separate email addresses, maybe then it is possible for me to turn off alerts for work email after working hours and turn it back on the next day when the workday starts (maybe with some tools it&apos;s even possible to automate this). If nothing else, it will help me to keep my personal email private.&lt;/p&gt;
&lt;h2&gt;My setup&lt;/h2&gt;
&lt;p&gt;This guide assumes you have a custom domain and it is in use somewhere. For me, I got this domain on Netlify DNS. Getting this done on their site was very simple, I can&apos;t say about other services. But hopefully it will be simple enough for you as well!&lt;/p&gt;
&lt;h3&gt;Step 1. Pick your solution&lt;/h3&gt;
&lt;p&gt;There are a few ways of getting this done. I&apos;ve picked the easiest one &lt;a href=&quot;https://improvmx.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;improv.mx&lt;/a&gt;, but I&apos;d recommend you look at the other options. My personal favourites are services such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://gsuite.google.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Suit&lt;/a&gt; which costs money but arguably provides a better and more complete service. Since it includes 30GB Google Drive storage, video conferencing and etc.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.zoho.com/mail&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Zoho Mail&lt;/a&gt; is free for &quot;Up to five users,5GB/User, 25MB attachment limit. Web access only. Email hosting for a single domain&quot;. Yes, it is a lot of requirements, but for some people, it might be enough.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you choose to go with another service Google Suit or Zoho Mail, then the rest of the steps won&apos;t make much sense to you.&lt;/p&gt;
&lt;h3&gt;Step 2. Set up DNS records&lt;/h3&gt;
&lt;p&gt;The improv.mx website has really good instructions on how to get this done. Just follow the following steps and it will start forwarding your emails.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5geFamgUokJY7noaB4P4hn/9a906919bafa94d3812525561bb2c2c3/Screenshot_2019-04-22_at_21.29.19.png&quot; alt=&quot;Screenshot 2019-04-22 at 21.29.19&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;improv.mx will take a custom domain and alias for that domain (e.g. hello@domain.com) then any email sent to that email domain will be forwarded to a user-defined email address (e.g. user-defined@gmail.com).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have decided to use my personal gmail address for now, but the ideal setup would be to sign up to another random Google account like 93g93984@gmail.com and use that as the forwarded email address. This way you could have more control over the forwarded emails. An example could be what I mentioned above, turn off the notification for that account altogether when you go on holiday. Just write yourself a little note to turn it back on when you get back!&lt;/p&gt;
&lt;h3&gt;Step 3. Reply from forwarded email account&lt;/h3&gt;
&lt;p&gt;This step is pretty key considering there is no way for you to reply to any emails on improv.mx website. It would be pretty weird for people to send email to X email address, and get a reply from Y email address.&lt;/p&gt;
&lt;p&gt;How to do this you might ask? Luckily, improv.mx got us covered once again. Follow &lt;a href=&quot;https://improvmx.com/guides/send-emails-using-gmail&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this guide&lt;/a&gt; from improv.mx and get the reply from your custom email setup once and for all.&lt;/p&gt;
&lt;p&gt;Notes: There are quite a few steps involved, so I won&apos;t include any images here.&lt;/p&gt;
&lt;h3&gt;Step 4. Set up a filter in forwarded email account&lt;/h3&gt;
&lt;p&gt;This step is to prevent emails forwarded to this account from going into spam folder. Gmail seems to be very suspicious of this, and placed a few of my test emails to spam folder. I personally have followed &lt;a href=&quot;https://www.lifewire.com/how-to-whitelist-a-sender-or-domain-in-gmail-1172106&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this tutorial&lt;/a&gt; to get this setup, it was quick and painless. With this done, you will never have to worry your emails getting moved to spam by mistake.&lt;/p&gt;
&lt;h3&gt;Be warned!&lt;/h3&gt;
&lt;p&gt;I tested this solution and it worked great! But it is worth noting on improv.mx website they &lt;strong&gt;only guarantee 99% uptime&lt;/strong&gt;. This means when their services are down, and someone send you an email at the same time, you will never receive that email. Unfortunate, I was not able to test this out since it would require improv.mx service to be offline. But my expectation is that, the sender&apos;s email will failed to send and they would know about it, and hopefully could contact you through another method.&lt;/p&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;I&apos;m personally looking forward to start giving out my own custom email address to people. It will also be a lot easier to give to people on the phone as well. Hope this was useful!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Web API: appendChild V.S. createDocumentFragment]]></title><description><![CDATA[<p>All the JavaScript rendering libraries and frameworks we use, all boils down to the fundamental web APIs at the end of the day. People are so used to work with the high-level React APIs or Vue syntaxes (me included), we forget the raw and interesting layer underneath it all. But not today!</p>]]></description><link>https://blog.hao.dev/web-api-appendchild-v-s-createdocumentfragment</link><guid isPermaLink="false">https://blog.hao.dev/web-api-appendchild-v-s-createdocumentfragment</guid><pubDate>Tue, 16 Apr 2019 23:05:00 GMT</pubDate><content:encoded>&lt;p&gt;All the JavaScript rendering libraries and frameworks we use, all boils down to the fundamental web APIs at the end of the day. People are so used to work with the high-level React APIs or Vue syntaxes (me included), we forget the raw and interesting layer underneath it all. But not today!&lt;/p&gt;&lt;p&gt;Today I&apos;m going to show you something that you&apos;ll probably never use again in your life as a JavaScript developer, something that will not earn you any new titles. All I can say is that it is interesting and it is fun!&lt;/p&gt;
&lt;p&gt;Now let&apos;s get straight into it.&lt;/p&gt;
&lt;h2&gt;How does the DOM work?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;The Document Object Model (DOM) is a programming interface for HTML and XML documents. It represents the page so that programs can change the document structure, style, and content. The DOM represents the document as nodes and objects. That way, programming languages can connect to the page. - MDN&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Everything that the user sees or doesn&apos;t see all exist on the DOM, to add or remove something new on to the DOM we can do something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// create node&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; node &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;div&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// append node to DOM&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;node&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is easy enough, but what about &lt;strong&gt;createDocumentFragment&lt;/strong&gt;?&lt;/p&gt;
&lt;p&gt;MDN documentation says that &lt;strong&gt;createDocumentFragment&lt;/strong&gt; API (most likely) has better performance than &lt;strong&gt;appendChild&lt;/strong&gt; API. Since a document fragment only exists in memory and is not part of the main DOM tree. This means appending children to it does not cause page reflow, thus gives better performance.&lt;/p&gt;
&lt;p&gt;So how much more performant are we talking about here? 10%, 20% or like 100%?! Let&apos;s do some experiments.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; totalNumOfNodes &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;100000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; parent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;parent&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; t0 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; totalNumOfNodes&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; child &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  child&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;textContent &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;test 1 - &lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;i&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; t1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; fragments &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createDocumentFragment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; totalNumOfNodes&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; i&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; child &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createElement&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;div&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  fragments&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;child&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
parent&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;fragments&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; t2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; performance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;now&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rendering took &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t1 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; t0&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; milliseconds.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;log&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Rendering took &quot;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t2 &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; t1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot; milliseconds.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;a href=&quot;https://codesandbox.io/s/51j4z40rk?fontsize=14&quot;&gt;
  &lt;img alt=&quot;Edit createDocumentFragment v.s. appendchild Performance&quot; src=&quot;https://codesandbox.io/static/img/play-codesandbox.svg&quot;&gt;
&lt;/a&gt;
&lt;p&gt;The results are shown in the graph below, I didn&apos;t want to try with even more nodes but it is easy to see the more nodes we add the more advantage &lt;strong&gt;createDocumentFragment&lt;/strong&gt; will have. &lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7UBJSBTcs5uzxSGZkuAXG/1db8d74f66c732190b54416b47d42e6e/Screenshot_2019-04-17_at_00.12.40.png&quot; alt=&quot;Screenshot 2019-04-17 at 00.12.40&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I haven&apos;t checked what the popular rendering libraries or frameworks are doing to optimise the addition of a massive number of nodes. But I can&apos;t imagine &lt;strong&gt;createDocumentFragment&lt;/strong&gt; isn&apos;t utilised to improve performance since people do care and research which has the &lt;a href=&quot;https://github.com/krausest/js-framework-benchmark&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;best performance&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Learning about &lt;strong&gt;createDocumentFragment&lt;/strong&gt; is probably not a very practical piece of knowledge. Most of us would never come across opportunities where this becomes an issue, but hey, if this comes up in an interview you will nail it!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Two practical Webpack performance optimisation]]></title><description><![CDATA[<p>If you have seen my previous blog on <a href="https://haodong.dev/a-quick-parcel-bundler-review" target="_blank" rel="nofollow noopener noreferrer">Parcel bundler</a> then you'd know my thoughts on Webpack. It is big, complicated and slow. It's big and complex because there are a lot of plugins for it and many ways to set it up as well as customising it. It is slow often due to the enterprise applications it is used in, simply because there are a lot of things going on in these projects than someone's SPA (single page application) personal project. Nobody wants to wait more than 1 min for the build process, here I'll be talking about a couple of practical hacks to boost the Webpack build performance used in a recent project.</p>]]></description><link>https://blog.hao.dev/two-practical-webpack-performance-optimisation</link><guid isPermaLink="false">https://blog.hao.dev/two-practical-webpack-performance-optimisation</guid><pubDate>Tue, 09 Apr 2019 18:27:00 GMT</pubDate><content:encoded>&lt;p&gt;If you have seen my previous blog on &lt;a href=&quot;https://haodong.dev/a-quick-parcel-bundler-review&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Parcel bundler&lt;/a&gt; then you&apos;d know my thoughts on Webpack. It is big, complicated and slow. It&apos;s big and complex because there are a lot of plugins for it and many ways to set it up as well as customising it. It is slow often due to the enterprise applications it is used in, simply because there are a lot of things going on in these projects than someone&apos;s SPA (single page application) personal project. Nobody wants to wait more than 1 min for the build process, here I&apos;ll be talking about a couple of practical hacks to boost the Webpack build performance used in a recent project.&lt;/p&gt;&lt;h2&gt;Parallelise When Possible&lt;/h2&gt;
&lt;p&gt;The project is a bit obscure, instead of building and bundling the application into one single minified JS file. Due to the number of region and device supported, we need to build multiple times to satisfy these requirements.&lt;/p&gt;
&lt;p&gt;Here is a simple example of the output (we are ignoring device for now for simplicity):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;.
+-- index.html
+-- some-other-file.xx
+-- region1
|   +-- bundle.js
|   +-- some-other-file.xx
+-- region2
|   +-- bundle.js
|   +-- some-other-file.xx
+-- region3
|   +-- bundle.js
|   +-- some-other-file.xx&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;There&apos;s a loop that returns an array of configs which Webpack will take and start building all of them sequentially. This is bad, and we need to parallelise this!&lt;/p&gt;
&lt;p&gt;If you are not too sure about &lt;a href=&quot;https://en.wikipedia.org/wiki/Parallel_computing&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;parallel computing&lt;/a&gt;. Imagine someone drops 100 pens on the floor, two people will always pick up these pens faster than one person (assume everyone picks up pens at the same rate). So the more people we can add to this job, the faster it will get done (until you hit the &lt;a href=&quot;https://en.wikipedia.org/wiki/Parallel_slowdown&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;parallel slowdown&lt;/a&gt; phenomenon, but that&apos;s for another time).&lt;/p&gt;
&lt;p&gt;There is a tool called &lt;a href=&quot;https://github.com/trivago/parallel-webpack&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;parallel-webpack&lt;/a&gt; which accepts the same input as Webpack but runs them in parallel utilising all the CPU threads. Since the input is basically the same, as long as you are providing an array of Webpack configs it will work out of the box with only the following changes:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// instead of doing
webpack --config=webpack.config.dev.js

// just change it to
parallel-webpack --config=webpack.config.dev.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This alone took our build time from more than 65 seconds to around 35 seconds which is about &lt;strong&gt;46% performance uplift&lt;/strong&gt;!&lt;/p&gt;
&lt;p&gt;If you want to go a step further, according to the parallel-webpack official documentation, turning off statistics also improves performance (version 1.3.0+).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;// just change it to
parallel-webpack --no-stats --config=webpack.config.dev.js&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Loader Caching&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;babel-loader caches its results in node_modules/.cache/babel-loader by default. After enabling caching with babel-loader we improved the build performance by another 26%. Although configurations vary among locales, most of the modules are the same. By enabling caching, expensive babel recompilations are avoided for subsequent locales being processed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I read the above from &lt;a href=&quot;https://blog.box.com/blog/how-we-improved-webpack-build-performance-95&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this blog&lt;/a&gt; that claims by caching Babel they were able to achieve a considerable performance gain. I did some simple benchmark tests and here are my results:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Type of build&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;1st test&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;2nd test&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;3rd test&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;4th test&lt;/th&gt;
&lt;th align=&quot;center&quot;&gt;Average&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Without cache build times&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;35.295 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;30.551 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;32.12 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;30.867 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;32.208 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;With cache build times&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;26.944 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;21.766 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;25.93 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;20.175 sec&lt;/td&gt;
&lt;td align=&quot;center&quot;&gt;23.703 sec&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The overall &lt;strong&gt;performance increase was 26.3%&lt;/strong&gt;, the blog above was spot on!&lt;/p&gt;
&lt;p&gt;Note: In this project, we only use &lt;code class=&quot;language-text&quot;&gt;babel-loader&lt;/code&gt;, but there is no reason why this technique cannot be applied to other loaders.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;This was just me scratching the surface of Webpack optimisation, I&apos;m sure there are hundreds of other things I could be doing to reduce the build time further. But since now on average the build time took less than 30 seconds, it wasn&apos;t worth me spending more time tweaking the setup further.&lt;/p&gt;
&lt;p&gt;For anyone who&apos;s Webpack build time is frustratingly slow, I&apos;d recommend them trying out these two hacks. If these don&apos;t apply then be sure to checkout Webpack&apos;s own &lt;a href=&quot;https://webpack.js.org/guides/build-performance&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;build performance&lt;/a&gt; documentation.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My blogging workflow]]></title><description><![CDATA[<p>I started to blog around the end of 2015, 3 years later it has been an interesting journey. I think I benefited quite a lot from it and thought I'd share my experience and some of my blogging practices with people who are looking to start blogging or simply want some inspirations.</p>]]></description><link>https://blog.hao.dev/my-blogging-workflow</link><guid isPermaLink="false">https://blog.hao.dev/my-blogging-workflow</guid><pubDate>Thu, 21 Mar 2019 19:21:00 GMT</pubDate><content:encoded>&lt;p&gt;I started to blog around the end of 2015, 3 years later it has been an interesting journey. I think I benefited quite a lot from it and thought I&apos;d share my experience and some of my blogging practices with people who are looking to start blogging or simply want some inspirations.&lt;/p&gt;&lt;h2&gt;Why blog&lt;/h2&gt;
&lt;p&gt;One main reason for me to start blogging at the time was to start building my own &quot;brand&quot;. Just like Github account with a lot of open source contributions, this blog was meant to help me along in my career. 3 years later, I can confirm it was a good decision.&lt;/p&gt;
&lt;p&gt;For people who are already in a comfortable job that they&apos;ll never see themselves leave, there are other reasons to blog too. I didn&apos;t have these as goals when I decided to blog, instead, I discovered them along the way:&lt;/p&gt;
&lt;h3&gt;Improves writing&lt;/h3&gt;
&lt;p&gt;Surprise, surprise, writing blogs helps to improve written communication skills. Simply blogging a couple of times a month, I can feel my writing getting better over time. At work, I also found writing up documentation or meeting notes a lot easier than before.&lt;/p&gt;
&lt;h3&gt;Learning&lt;/h3&gt;
&lt;p&gt;Setting up the blog requires time and effort, but maintaining it is more stressful. I initially tried to blog using an open source software called &lt;a href=&quot;https://ghost.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ghost Blog&lt;/a&gt;. But it turns out it is a lot of hassle to go in and update the software every few weeks. The update process (at the time) required SSH into the virtual server and enter terminal commands. Then I move on to Hexo static site generator and hosting it on Github Pages, I also tried Hugo and finally settled on the &lt;a href=&quot;/migrated-blog-to-gatsby-and-contentful&quot;&gt;current solution&lt;/a&gt; with Gatsby.&lt;/p&gt;
&lt;p&gt;Would I have learned all this if I didn&apos;t blog? Maybe, but that would be very unlikely since there&apos;s very little reason to touch these kinds of tech stack at work. Setting up a blog is a personal project in a way that requires nurturing. Thinking of ways to improve it will test and improve your technical abilities along the way.&lt;/p&gt;
&lt;p&gt;Some other things I learned as part of the blog set up:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Google Analytics (GA)&lt;/li&gt;
&lt;li&gt;Search Engine Optimisation (SEO)&lt;/li&gt;
&lt;li&gt;Preserve URL after site migration through redirects&lt;/li&gt;
&lt;li&gt;JAMstack and benefits of static sites&lt;/li&gt;
&lt;li&gt;And etc&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I hope the above reasons are enough to convince people that they should start blogging. Don&apos;t worry if you haven&apos;t done it before. If you&apos;ve seen my previous blog posts then you&apos;d know my blog at the start felt more like tweets than actual posts because they were so short. At the start just do the best you can, because with practice you can do it as well as anyone.&lt;/p&gt;
&lt;h2&gt;Coming up a blog idea&lt;/h2&gt;
&lt;p&gt;You probably have seen blogs on Medium being shared around like crazy and getting thousands of likes. How can they think of good topics to blog about? What if no one likes what you write about?&lt;/p&gt;
&lt;p&gt;My blog heading is called &quot;Hao&apos;s learning log&quot; for a reason. I intended it to be a place where I write down my own thoughts, in another word it is for me. If I write something that I care about and no one else finds it interesting, then that&apos;s fine. Until today most of my posts don&apos;t get any views. But when I look back I can see what I was thinking at a time and how my views have changed over time. &lt;/p&gt;
&lt;p&gt;If you really find it hard to think of a cool topic, here is where I get my ideas from:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What are you working on at the moment? Anything cool? Anything different? Challenges? Did you learn anything new? All these could be a blog post, if you start paying attention you might find that you have too many ideas to blog about.&lt;/li&gt;
&lt;li&gt;Check out the trending topics online. What is the most popular framework? What is becoming popular? In my experience, the blog posts I got most of my views on are Vue related. That was only because I started blogging about it at the early stages and it scored high on search engine ranking.&lt;/li&gt;
&lt;li&gt;If you find a popular topic or discussion and find yourself with a different perspective on it. Rather than writing a long message that boosts other people&apos;s popularity consider writing a blog post about it.&lt;/li&gt;
&lt;li&gt;Watch YouTube videos, listen to Podcasts, read books and etc. There are plenty of places to find inspirations from.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Write it&lt;/h2&gt;
&lt;p&gt;Most of the time I do too much thinking and not enough writing, so I recommend others not to be like me. If you have an idea start jotting down points and if you find there&apos;s enough content then just write up a draft. If you really don&apos;t like the content after that, then you don&apos;t have to post it. But most of the time, getting it out of your head is the important first step.&lt;/p&gt;
&lt;p&gt;You&apos;ll want to do this in a way that is quick and clean. I personally prefer to use Markdown because I will then focus on the writing instead of wasting time tweaking font size, colour and etc. Another reason for using Markdown is because Contentful and Gatsby both support Markdown, so works even better for me.&lt;/p&gt;
&lt;p&gt;If you never used Markdown before here are a few nice tools to write in Markdown:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://code.visualstudio.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;VS Code&lt;/a&gt; is a text editor, so it is just as good for writing Markdown content&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://stackedit.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;StackEdit&lt;/a&gt; is an online-only Markdown Editor, so great for people switch between machines all the time.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://typora.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;typora&lt;/a&gt; is a desktop application available on Window, Linux and Mac (beta).&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.notion.so&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Notion&lt;/a&gt; is more than just a markdown editor, it might be an overkill for some. The only downside is that copying text out of Notion has always been a pain for me. Since their format messes up Markdown links.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Checks&lt;/h2&gt;
&lt;p&gt;Sometimes it is hard to find someone to proofread your content for you. I am always posting blogs at weird times, and I don&apos;t like to bother people. I used to just post whatever I write online, and often I&apos;d find mistakes later. If you are like me, I&apos;d recommend using a tool called Grammarly which could correct some simple grammar and spelling mistakes.&lt;/p&gt;
&lt;p&gt;It means I often need to copy what I write around, I am looking to find a good way around this. But so far, this is my workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Write up content in Markdown&lt;/li&gt;
&lt;li&gt;Copy it into Grammarly for simple Checks&lt;/li&gt;
&lt;li&gt;When I&apos;m happy, copy it into Contentful which is what I&apos;m using to store my blog posts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One thing I&apos;d like to mention which can be stressful is working with images. Because I need to copy my content around a few times, it means I cannot insert the images into the article as I write. I normally add in placeholders for images and then add them in when the content is in Contentful. So I always find there is some assembling work in the end. &lt;/p&gt;
&lt;p&gt;With this workflow, I normally find I prefer to write shorter posts that can be done in a few hours instead of a longer article which may span over a few days. It is not impossible, just I prefer to sit down and have everything done in one session.&lt;/p&gt;
&lt;h2&gt;Deploy&lt;/h2&gt;
&lt;p&gt;Deployment is very easy with my current setup it is one of the reasons why I settled with it. I won&apos;t go in too much detail here but get in touch if you&apos;d like to know more. I&apos;ve also written a &lt;a href=&quot;/migrated-blog-to-gatsby-and-contentful&quot;&gt;blog post&lt;/a&gt; on this already and explained why I decided to change to it.&lt;/p&gt;
&lt;p&gt;The tech stack includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Github&lt;/li&gt;
&lt;li&gt;Netlify&lt;/li&gt;
&lt;li&gt;Contentful&lt;/li&gt;
&lt;li&gt;Gatsby&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;When I add a blog post into Contentful and press publish. It will trigger a Netlify build via webhook, which takes the Gatsby blog code and generates a static site using the data coming from Contentful. So apart from the initial setup and some bug fixes here and there. There is very little technical maintenance work.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Netlify build error: vue-cli-service not found]]></title><description><![CDATA[<p>I recently created a project using Vue CLI 3, and after got the project set up to a basic state I decided to add it onto Netlify and get the builds going for future reference (Netlify keeps all previous builds and if they are static sites then it also serves them at generated URLs). I started doing this late at night, and after battling it for a few hours I still didn't get anywhere. It was a pretty stressful situation, I just couldn't work out why I kept getting <code class="language-text">vue-cli-service: not found</code> errors when Netlify runs the build. There was surprising little help online for this, maybe not many people have started using Vue CLI 3 with Netlify yet? I only managed to find out the reason after more digging and thought I'd share my solution so no one else needs to go through the same amount of pain.</p>]]></description><link>https://blog.hao.dev/netlify-build-error-vue-cli-service-not-found</link><guid isPermaLink="false">https://blog.hao.dev/netlify-build-error-vue-cli-service-not-found</guid><pubDate>Sat, 16 Mar 2019 11:37:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently created a project using Vue CLI 3, and after got the project set up to a basic state I decided to add it onto Netlify and get the builds going for future reference (Netlify keeps all previous builds and if they are static sites then it also serves them at generated URLs). I started doing this late at night, and after battling it for a few hours I still didn&apos;t get anywhere. It was a pretty stressful situation, I just couldn&apos;t work out why I kept getting &lt;code class=&quot;language-text&quot;&gt;vue-cli-service: not found&lt;/code&gt; errors when Netlify runs the build. There was surprising little help online for this, maybe not many people have started using Vue CLI 3 with Netlify yet? I only managed to find out the reason after more digging and thought I&apos;d share my solution so no one else needs to go through the same amount of pain.&lt;/p&gt;&lt;h2&gt;The problem&lt;/h2&gt;
&lt;p&gt;Here is the full error log from Netlify:&lt;/p&gt;
&lt;p&gt;PS: As you can see it is 2:18 am&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;2:21:14 AM: Build ready to start
2:21:16 AM: build-image version: 324ec043422499a87b63cac1f1dabeefe6dca19d
2:21:16 AM: build-image tag: v3.0.2
2:21:16 AM: buildbot version: 867b17c4ea5ef955db899d19b431b26c9d57e87d
2:21:16 AM: Fetching cached dependencies
2:21:16 AM: Starting to download cache of 254.8KB
2:21:16 AM: Finished downloading cache in 94.089461ms
2:21:16 AM: Starting to extract cache
2:21:16 AM: Failed to fetch cache, continuing with build
2:21:16 AM: Starting to prepare the repo for build
2:21:17 AM: No cached dependencies found. Cloning fresh repo
2:21:17 AM: git clone https://github.com/h-dong/clean-slate
2:21:18 AM: Preparing Git Reference refs/heads/master
2:21:18 AM: Starting build script
2:21:18 AM: Installing dependencies
2:21:20 AM: Downloading and installing node v8.15.1...
2:21:20 AM: Downloading https://nodejs.org/dist/v8.15.1/node-v8.15.1-linux-x64.tar.xz...
2:21:20 AM: 
                                                                           0.0%
2:21:21 AM: 
##                                                                         3.3%
2:21:21 AM: 
######################################################
2:21:21 AM:  75.9%
2:21:21 AM: 
######################################################################## 100.0%
2:21:21 AM: Computing checksum with sha256sum
2:21:21 AM: Checksums matched!
2:21:23 AM: Now using node v8.15.1 (npm v6.4.1)
2:21:24 AM: Attempting ruby version 2.3.6, read from environment
2:21:25 AM: 
2:21:25 AM: ** WARNING **
2:21:25 AM: Using custom ruby version 2.3.6, this will slow down the build.
2:21:25 AM: To ensure fast builds, set the RUBY_VERSION environment variable, or .ruby-version file, to an included ruby version.
2:21:25 AM: Included versions: 2.6.2
2:21:25 AM: 
2:21:25 AM: Required ruby-2.3.6 is not installed - installing.
2:21:26 AM: Searching for binary rubies, this might take some time.
2:21:26 AM: Found remote file https://rvm_io.global.ssl.fastly.net/binaries/ubuntu/16.04/x86_64/ruby-2.3.6.tar.bz2
2:21:26 AM: Checking requirements for ubuntu.
2:21:27 AM: Requirements installation successful.
2:21:27 AM: ruby-2.3.6 - #configure
2:21:27 AM: ruby-2.3.6 - #download
2:21:28 AM: ruby-2.3.6 - #validate archive
2:21:40 AM: ruby-2.3.6 - #extract
2:21:46 AM: ruby-2.3.6 - #validate binary
2:21:47 AM: ruby-2.3.6 - #setup
2:21:47 AM: ruby-2.3.6 - #gemset created /opt/buildhome/.rvm/gems/ruby-2.3.6@global
2:21:47 AM: ruby-2.3.6 - #importing gemset /opt/buildhome/.rvm/gemsets/global.gems
2:21:49 AM: ............................................................
2:21:49 AM: ruby-2.3.6 - #generating global wrappers
2:21:50 AM: .......
2:21:50 AM: ruby-2.3.6 - #gemset created /opt/buildhome/.rvm/gems/ruby-2.3.6
2:21:50 AM: ruby-2.3.6 - #importing gemsetfile /opt/buildhome/.rvm/gemsets/default.gems evaluated to empty gem list
2:21:50 AM: ruby-2.3.6 - #generating default wrappers
2:21:50 AM: .......
2:21:51 AM: Using /opt/buildhome/.rvm/gems/ruby-2.3.6
2:21:51 AM: Using ruby version 2.3.6
2:21:51 AM: Using PHP version 5.6
2:21:51 AM: Started restoring cached node modules
2:21:51 AM: Finished restoring cached node modules
2:21:52 AM: Installing NPM modules using NPM version 6.4.1
2:22:04 AM: &gt; grpc@1.18.0 install /opt/build/repo/node_modules/grpc
2:22:04 AM: &gt; node-pre-gyp install --fallback-to-build --library=static_library
2:22:05 AM: node-pre-gyp
2:22:05 AM:  WARN
2:22:05 AM:  Using needle for node-pre-gyp https download
2:22:05 AM: [grpc] Success: &quot;/opt/build/repo/node_modules/grpc/src/node/extension_binary/node-v57-linux-x64-glibc/grpc_node.node&quot; is installed via remote
2:22:07 AM: added 154 packages from 65 contributors and audited 41523 packages in 13.961s
2:22:07 AM: found 64 low severity vulnerabilities
2:22:07 AM:   run `npm audit fix` to fix them, or `npm audit` for details
2:22:07 AM: NPM modules installed
2:22:07 AM: Started restoring cached go cache
2:22:07 AM: Finished restoring cached go cache
2:22:07 AM: Installing Go version 1.10
2:22:12 AM: unset GOOS;
2:22:12 AM: unset GOARCH;
2:22:12 AM: export GOROOT=&apos;/opt/buildhome/.gimme_cache/versions/go1.10.linux.amd64&apos;;
2:22:12 AM: export PATH=&quot;/opt/buildhome/.gimme_cache/versions/go1.10.linux.amd64/bin:${PATH}&quot;;
2:22:12 AM: go version &gt;&amp;amp;2;
2:22:12 AM: export GIMME_ENV=&quot;/opt/buildhome/.gimme_cache/env/go1.10.linux.amd64.env&quot;
2:22:12 AM: go version go1.10 linux/amd64
2:22:12 AM: Installing missing commands
2:22:12 AM: Verify run directory
2:22:12 AM: Executing user command: npm run build
2:22:14 AM: &gt; clean-slate@0.1.0 build /opt/build/repo
2:22:14 AM: &gt; vue-cli-service build
2:22:14 AM: sh: 1: vue-cli-service: not found
2:22:14 AM: npm
2:22:14 AM: ERR! file sh
2:22:14 AM: npm ERR!
2:22:14 AM: code
2:22:14 AM:  ELIFECYCLE
2:22:14 AM: npm
2:22:14 AM:  ERR!
2:22:14 AM: errno ENOENT
2:22:14 AM: npm ERR!
2:22:14 AM:  syscall
2:22:14 AM:  spawn
2:22:14 AM: npm ERR! clean-slate@0.1.0 build: `vue-cli-service build`
2:22:14 AM: npm ERR! spawn ENOENT
2:22:14 AM: npm ERR!
2:22:14 AM: npm
2:22:14 AM: ERR!
2:22:14 AM:  Failed at the clean-slate@0.1.0 build script.
2:22:14 AM: npm
2:22:14 AM:  ERR!
2:22:14 AM:  This is probably not a problem with npm. There is likely additional logging output above.
2:22:15 AM: npm ERR!
2:22:15 AM:  A complete log of this run can be found in:
2:22:15 AM: npm ERR!     /opt/buildhome/.npm/_logs/2019-03-17T02_22_14_316Z-debug.log
2:22:15 AM: Caching artifacts
2:22:15 AM: Started saving node modules
2:22:15 AM: Finished saving node modules
2:22:15 AM: Started saving pip cache
2:22:15 AM: Finished saving pip cache
2:22:15 AM: Started saving emacs cask dependencies
2:22:15 AM: Finished saving emacs cask dependencies
2:22:15 AM: Started saving maven dependencies
2:22:15 AM: Finished saving maven dependencies
2:22:15 AM: Started saving boot dependencies
2:22:15 AM: Finished saving boot dependencies
2:22:15 AM: Started saving go dependencies
2:22:16 AM: Finished saving go dependencies
2:22:17 AM: Cached node version v8.15.1
2:22:17 AM: Cached ruby version 2.3.6
2:22:18 AM: Error running command: Build script returned non-zero exit code: 1
2:22:18 AM: Failing build: Failed to build site
2:22:18 AM: failed during stage &apos;building site&apos;: Build script returned non-zero exit code: 1
2:22:18 AM: Finished processing build request in 1m1.487616832s
2:22:18 AM: Shutting down logging, 0 messages pending&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I ran the build for at least 5 times, but each time I get the same result. I tried updating dependencies, I tried adding &lt;code class=&quot;language-text&quot;&gt;@vue/cli&lt;/code&gt; as a dependency, and etc. It was late and I tried a whole bunch other &quot;solutions&quot; which I can&apos;t recall now. The moral of the story is that nothing worked!&lt;/p&gt;
&lt;p&gt;I tried building locally, it works. &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;bash&quot;&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;$ &lt;span class=&quot;token function&quot;&gt;npm&lt;/span&gt; run build                           

⠋  Building &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; production&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;.

 WARNING  Compiled with &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt; warnings                                                                                                                                 03:09:34

 warning  

asset size limit: The following asset&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; exceed the recommended size limit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;244&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;.
This can impact web performance.
Assets: 
  js/chunk-vendors.29d7585c.js &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;716&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

 warning  

entrypoint size limit: The following entrypoint&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; combined asset size exceeds the recommended limit &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;244&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;. This can impact web performance.
Entrypoints:
  app &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;734&lt;/span&gt; KiB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      js/chunk-vendors.29d7585c.js
      css/app.8a47e692.css
      js/app.77cc03b0.js


 warning  

webpack performance recommendations: 
You can limit the size of your bundles by using import&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; or require.ensure to lazy load some parts of your application.
For &lt;span class=&quot;token function&quot;&gt;more&lt;/span&gt; info visit https://webpack.js.org/guides/code-splitting/

  File                                      Size             Gzipped

  dist/js/chunk-vendors.29d7585c.js         &lt;span class=&quot;token number&quot;&gt;716.30&lt;/span&gt; KiB       &lt;span class=&quot;token number&quot;&gt;198.37&lt;/span&gt; KiB
  dist/js/app.77cc03b0.js                   &lt;span class=&quot;token number&quot;&gt;11.87&lt;/span&gt; KiB        &lt;span class=&quot;token number&quot;&gt;4.36&lt;/span&gt; KiB
  dist/service-worker.js                    &lt;span class=&quot;token number&quot;&gt;0.95&lt;/span&gt; KiB         &lt;span class=&quot;token number&quot;&gt;0.53&lt;/span&gt; KiB
  dist/precache-manifest.0eba1e4600aba3b    &lt;span class=&quot;token number&quot;&gt;0.45&lt;/span&gt; KiB         &lt;span class=&quot;token number&quot;&gt;0.24&lt;/span&gt; KiB
  67c18f1ca50ecba92.js
  dist/css/app.8a47e692.css                 &lt;span class=&quot;token number&quot;&gt;6.15&lt;/span&gt; KiB         &lt;span class=&quot;token number&quot;&gt;1.52&lt;/span&gt; KiB

  Images and other types of assets omitted.

 DONE  Build complete. The dist directory is ready to be deployed.
 INFO  Check out deployment instructions at https://cli.vuejs.org/guide/deployment.html&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Hmmm... I tried building it using Travis, guess what it works!&lt;/p&gt;
&lt;p&gt;So what is the issue then? It must be Netlify! If it weren&apos;t late in the night I totally would have spammed Netlify with some (possibly passible aggressive) messages. But I left it and went to bed instead.&lt;/p&gt;
&lt;h2&gt;The solution&lt;/h2&gt;
&lt;p&gt;The next day, after some most needed sleep and more digging around I found the issue! As expected, it was simple and stupid.&lt;/p&gt;
&lt;p&gt;I asked Vue CLI to generate a service worker for PWA support, inside &lt;code class=&quot;language-text&quot;&gt;registerServiceWorker.js&lt;/code&gt; it uses &lt;code class=&quot;language-text&quot;&gt;process.env.NODE_ENV === &quot;production&quot;&lt;/code&gt; to determine if service worker should get into action (do something). Similarly, in Vuex store I am also checking &lt;code class=&quot;language-text&quot;&gt;process.env.NODE_ENV === &quot;production&quot;&lt;/code&gt; to decide if I should turn on &lt;code class=&quot;language-text&quot;&gt;strict&lt;/code&gt; mode or not. Naturally, I felt &lt;code class=&quot;language-text&quot;&gt;NODE_ENV&lt;/code&gt; was important and should be declared as an environment variable on Netlify to ensure the app behaves correctly.&lt;/p&gt;
&lt;p&gt;Little did I know Netlify does something special when you add a &lt;code class=&quot;language-text&quot;&gt;process.env.NODE_ENV&lt;/code&gt; build environment variable. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you manually set the NODE_ENV to production, any devDependencies in your package.json will NOT be installed when we automatically run yarn install or npm install at the beginning of the build.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So basically since none of my devDependencies were even getting installed, therefore the build failing should be no surprise. I just wish I found and read Netlify&apos;s documentation a bit sooner.&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://www.netlify.com/docs/build-gotchas/#devdependencies&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Netlify build gochas devDependeices&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Web Application Rapid Prototyping]]></title><description><![CDATA[<p>First of all, I just want to say that I love the concept of rapid prototyping!</p>
<p>Why? There are so many reasons, but here are just a few which I value deeply:</p>
<ol>
<li>Different to normal development - It gives you a whole new perspective to software development, normal expectations and assumptions are not valid here. Testing? Hmmm, maybe later. Team chat about coding standard? No need, we will use someone else's lint rules.</li>
<li>Learning opportunities - It really tests your knowledge of different toolsets, libraries, and services. If you are working with other people, then there is a high chance of you picking up something you didn't know before.</li>
<li>Achievement - Literally build something from nothing in a matter of hours or days.</li>
</ol>]]></description><link>https://blog.hao.dev/web-application-rapid-prototyping</link><guid isPermaLink="false">https://blog.hao.dev/web-application-rapid-prototyping</guid><pubDate>Mon, 11 Mar 2019 02:03:00 GMT</pubDate><content:encoded>&lt;p&gt;First of all, I just want to say that I love the concept of rapid prototyping!&lt;/p&gt;
&lt;p&gt;Why? There are so many reasons, but here are just a few which I value deeply:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Different to normal development - It gives you a whole new perspective to software development, normal expectations and assumptions are not valid here. Testing? Hmmm, maybe later. Team chat about coding standard? No need, we will use someone else&apos;s lint rules.&lt;/li&gt;
&lt;li&gt;Learning opportunities - It really tests your knowledge of different toolsets, libraries, and services. If you are working with other people, then there is a high chance of you picking up something you didn&apos;t know before.&lt;/li&gt;
&lt;li&gt;Achievement - Literally build something from nothing in a matter of hours or days.&lt;/li&gt;
&lt;/ol&gt;&lt;p&gt;It might not get talked about a lot, but the use cases are probably more common than you think. It could be creating a proof of concept for a client about a potential project, doing a 24h or 48h hackathon or just get out some crazy personal projects like &quot;implement the next Facebook app over a weekend&quot;. Either way, it takes a great number of trial and error before it&apos;s possible to narrow down to a set of tools and libraries which will ensure the &quot;rapid&quot; part of prototyping. In this blog, I&apos;ll be sharing my favourite tools and set up for such scenarios.&lt;/p&gt;
&lt;h2&gt;What it means to be rapid?&lt;/h2&gt;
&lt;p&gt;To be rapid, we need to cut down as much work as possible (duh!). This means anything that has nothing to do with building the actual product (look, feel and functionality) is dead weight. Even with the aforementioned three things, if we can avoid reinventing the wheel when we should. Here are some things to consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don&apos;t spend ages setting up a custom webpack build script, project configs or install NPM dependencies manually. Use a CLI or something similar to generate a workable project in minutes, then build the app and deliver value. Don&apos;t be afraid of standing on the shoulders of giants!&lt;/li&gt;
&lt;li&gt;Use a UI library instead of writing custom styling, or take it to the next level using a component library. Which will even save you time from adding styling to components such as buttons and form elements.&lt;/li&gt;
&lt;li&gt;Don&apos;t build your own CI/CD pipeline, often it takes time to put together or configure. Time is too precious to be used for these kinds of activities, use an off-the-shelf product. For prototypes, I think 99% of the time they will be more than enough.&lt;/li&gt;
&lt;li&gt;Don&apos;t build custom APIs, and don&apos;t spin up your own Databases. There is also the off-the-shelf solution that are simply too brilliant to ignore.&lt;/li&gt;
&lt;li&gt;At this stage often there aren&apos;t enough people working on the same codebase, so there should not be a lot of friction. Keep the releases regular and iterate quickly. I recommend using the &lt;a href=&quot;https://guides.github.com/introduction/flow/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github flow&lt;/a&gt; for any projects.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then the interesting question is since there are so many libraries, tools and SaaS(s?), what is the easiest, fastest and most scalable way to create a web app? That is not only easy to set up but also easy to iterate on top of and scale up (if we hit the jackpot!) to be able to serve to more users. Some may question why scalability is a concern here, I would return the question and ask &quot;what if you can get it with this set up for free?&quot; :)&lt;/p&gt;
&lt;h2&gt;Steps we can&apos;t bypass&lt;/h2&gt;
&lt;p&gt;No matter how much we simplify the process there will always be a few areas we cannot remove completely. These are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Creating the application - implementing features that differentiates itself from other products on the market, otherwise, there is no need of creating this prototype right?&lt;/li&gt;
&lt;li&gt;Production environment setup - the code must be hosted somewhere in one form or another. So that when the user enters an URL they can visit the application.&lt;/li&gt;
&lt;li&gt;Release new changes - there must be a way to have one version on the production environment, whilst developing new features. Then when the new feature is ready, release the new feature to the production environment.&lt;/li&gt;
&lt;li&gt;Dynamic functionalities - be able to save data and provide a complex and dynamic user experience, most likely through using external APIs and SaaS.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;We will cover each of the above in more depth in the following sections.&lt;/p&gt;
&lt;h2&gt;Easiest way to creating the application&lt;/h2&gt;
&lt;p&gt;The simplest way to creating web apps is using something like &lt;code class=&quot;language-text&quot;&gt;create-react-app&lt;/code&gt; repo as a starting point or simply generate a get started project using &lt;code class=&quot;language-text&quot;&gt;Vue CLI&lt;/code&gt; . I am not an Angular developer, but there is also the &lt;code class=&quot;language-text&quot;&gt;Angular CLI&lt;/code&gt; tool as well.&lt;/p&gt;
&lt;p&gt;In this case, I would use something like &lt;code class=&quot;language-text&quot;&gt;Vue CLI&lt;/code&gt;. I won&apos;t cover too much detail here, because I&apos;ve already covered it in another &lt;a href=&quot;/a-quick-overview-of-creating-a-new-project-with-vue-cli/&quot;&gt;blog post&lt;/a&gt;. It&apos;s a simple 10 steps even if you want to configure everything yourself.&lt;/p&gt;
&lt;p&gt;If getting the ideas out there is a top priority, then even consider not including things such as E2E testing. Although, I&apos;d recommend against that if there&apos;s a chance of building on top of this in the future. Maybe the best option is to include it in the setup but not add any test until a decision has been made, as it is always easier to remove than to add later.&lt;/p&gt;
&lt;p&gt;I have deliberately left out UI and component libraries because there are so many out there, so just pick one that works best for you.&lt;/p&gt;
&lt;h2&gt;App Hosting&lt;/h2&gt;
&lt;p&gt;Developers no longer need to pay for app hosting. There are so many free or free-tier services on the market now, so don&apos;t waste money on this.&lt;/p&gt;
&lt;p&gt;There are a few that comes to mind, but the one I recommend is &lt;a href=&quot;https://www.netlify.com/features/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Netlify&lt;/a&gt;. Check out the link for more info, but just to mention a few key features:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automate build &amp;#x26; deployment based on webhook&lt;/li&gt;
&lt;li&gt;Each push to preview your changes and a  Netlify URL for all your branches&lt;/li&gt;
&lt;li&gt;Built-in SSL/TLS and certification (Let&apos;s Encrypt)&lt;/li&gt;
&lt;li&gt;And many more&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The setup process is also very straight forward. Login with Github, then select the repository you&apos;d like to set up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2AgqEYvLtHqP1x2yTRkrPo/f020020467a49103f6387b9d8d195c06/Screenshot_2019-03-11_at_00.39.45.png&quot; alt=&quot;Screenshot 2019-03-11 at 00.39.45&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7FG1G7553PWMqllCU4ZqcB/b584f86d8302bbafa934e58a767cef72/Screenshot_2019-03-11_at_00.40.19.png&quot; alt=&quot;Screenshot 2019-03-11 at 00.40.19&quot;&gt;&lt;/p&gt;
&lt;p&gt;Enter in the build command (e.g. npm run build) and folder where the app is generated (e.g. dist folder). Voila!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7HQo4YKaCrHj06umkcTreR/b0bba366ad05fcc6bf414ec4ca4b871b/Screenshot_2019-03-11_at_00.41.10.png&quot; alt=&quot;Screenshot 2019-03-11 at 00.41.10&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course, there are lot more settings for you to custom (like previously mentioned SSL/TLS certificates, custom domain and etc), but this is all it takes to get everything started.&lt;/p&gt;
&lt;h2&gt;Release new versions&lt;/h2&gt;
&lt;p&gt;Not much to say here. With Netlify, it automatically creates webhook with Github so that when the code is pushed (to any branch) it could trigger a build. This means when you want to merge a PR, it will also tell you if the code is working correctly.&lt;/p&gt;
&lt;h2&gt;Dynamic functionalities&lt;/h2&gt;
&lt;p&gt;With any web application, we will need to use APIs or other means to communicate with a database in order to read and write user data. This is to ensure when the user return, they can resume from where they left off. In other cases, it could be used to provide the user information to improve their experience. E.g. typeahead with a list of countries in a country form field instead of asking the user typing it in manually.&lt;/p&gt;
&lt;p&gt;The service I like the most is Google&apos;s Firebase. It has so much features baked in, things like OAuth, sign up, forget password and etc. But most importantly, its real-time database. This will allow us to build the application that can change on user&apos;s device in real time (hence the name real-time database). For example, if I have a chat app open on two devices. If I send a message on one of these devices then the other device will simultaneously show up without the user having to refresh the page or interact with the app. This is seriously powerful stuff, these kinds of features used to be a headache to build with &lt;a href=&quot;http://socket.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;socket.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The setup process for Firestore is very well documented by Google. Check out the &lt;a href=&quot;https://firebase.google.com/docs/firestore/quickstart&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official doc&lt;/a&gt;. If you are too lazy to click open the link, then TL;DR is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install NPM dependency&lt;/li&gt;
&lt;li&gt;Initialize Cloud Firestore by adding in API keys and called &lt;code class=&quot;language-text&quot;&gt;firebase.firestore()&lt;/code&gt; in the app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It worth noting the API keys should be version controlled because it will then forever be on Github or other source control tools. I&apos;ll cover the best way to set up Firebase in a different blog post, as it is not quite as relevant to rapid prototyping.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;To conclude, the best way to rapidly set up a CI/CD pipeline, codebase and production environment is by using the following tools:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Vue CLI&lt;/li&gt;
&lt;li&gt;Netlify&lt;/li&gt;
&lt;li&gt;Firebase&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here&apos;s a diagram illustrating how everything fits together.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4mqlhk2heUvkkpxOim8NPz/a9c3f1d96fd6819883cb76f6fd3f5078/Group_2_1_.png&quot; alt=&quot;Group 2(1)&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you run through the above steps a couple of times, I&apos;m sure it&apos;s possible to get a prototype project set up in less than 5 mins (excluding the Vue CLI dependencies build time of course).&lt;/p&gt;
&lt;h2&gt;Alternative tools&lt;/h2&gt;
&lt;p&gt;As mentioned already, there are simply too many tools out there for me to include in one set up. Most of them have overlapping use cases or is not as powerful as the tools mentioned above. But I am aware depending on the project idea, the requirement for tools can be very different so here are a few other tools which I&apos;ve been very impressed in the past:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://app.contentful.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Contentful&lt;/a&gt; - if what you are building requires a CMS, then this is a strong contender in this field. I am personally using this for this blog, and I can say a lot of great things about it.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://apiary.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Apiary&lt;/a&gt; - If you do need to create your own custom API for a project but don&apos;t want to actually waste time building it. This is a cool way to mock some responses then build the real ones later.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://cloudinary.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cloudnary&lt;/a&gt; - creating an image-heavy app? Definitely check out this service, told simply it hosts media files for you e.g. images and videos. But they allow dynamically change image size, rotations, cropping and etc via URL param or using their SDK.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I tried to keep the explanation short, so check out their websites for more information. They will definitely sell themselves than I ever could :D&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I&apos;m always looking for new cool tools, libraries and services. If you think I&apos;ve missed something, then I&apos;d love to know! Get hold of me either by leaving comments here or ping me on Twitter.&lt;/p&gt;
&lt;p&gt;I hope this article was helpful, thanks for reading!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A quick overview of creating a new project with vue CLI]]></title><description><![CDATA[<p>Vue CLI is a powerful tool, designed to help developers to set up a base project on which they could start building on top of with extreme ease. It offers a bunch of configuration options which caters for different people's preferences and needs. However, as someone who's new to Vue or web development, it could be scary trying to go through the manual setup. This blog post attempts to change that, by explaining each step and give my personal recommendation for each configuration.</p>]]></description><link>https://blog.hao.dev/a-quick-overview-of-creating-a-new-project-with-vue-cli</link><guid isPermaLink="false">https://blog.hao.dev/a-quick-overview-of-creating-a-new-project-with-vue-cli</guid><pubDate>Sun, 10 Mar 2019 22:30:00 GMT</pubDate><content:encoded>&lt;p&gt;Vue CLI is a powerful tool, designed to help developers to set up a base project on which they could start building on top of with extreme ease. It offers a bunch of configuration options which caters for different people&apos;s preferences and needs. However, as someone who&apos;s new to Vue or web development, it could be scary trying to go through the manual setup. This blog post attempts to change that, by explaining each step and give my personal recommendation for each configuration.&lt;/p&gt;&lt;p&gt;Disclaimer: this setup is just what I prefer, it might not fit everyone&apos;s needs. Hopefully, it can act as a guideline for anyone who wants to hear a second opinion.&lt;/p&gt;
&lt;p&gt;Note: This blog assumes the CLI v3 has already been installed if you would like to find out more info on how to do that check out the &lt;a href=&quot;https://cli.vuejs.org/guide/installation.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official site&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Setup process&lt;/h2&gt;
&lt;p&gt;Navigate to the ideal location on your development machine, then run the following command in the terminal:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;vue create awesome-app&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then the rest is just selecting how you&apos;d like this project to be set up. Here are the steps and my choices.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1. Select a preset&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/18IKLYOJxvvGP1SK57rK7S/ed6a907789ebf7f73e2d242fb6480c24/Screenshot_2019-03-10_at_15.22.06.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.22.06&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I have decided to stick with my custom setup, so I selected &quot;Manually select features&quot;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Check features needed for your project&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2D3HWGLVkwQwNDdjhaKl1p/39b6aaaaa5b438e379781e6b0cee0745/Screenshot_2019-03-10_at_15.21.44.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.21.44&quot;&gt;&lt;/p&gt;
&lt;p&gt;  Here are my justification for my choices and why:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Babel&lt;/code&gt; - transpiles JavaScript for better backwards support. I would even urge people to consider not selecting this, especially if the older browsers support is not a requirement.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Typescript&lt;/code&gt; - I did not pick this because this is a small prototype app, and I did not want to spend time defining types and etc. But for a large project, I would recommend selecting this option.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Progressive Web App&lt;/code&gt; (PWA) Support - this is a no brainer, as more browser adopt this standard more and more people will start using this. It is important to have the shell in place even if you don&apos;t need to work on it right this minute.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Router&lt;/code&gt; - is your application going to be a single page application? Or would it benefit from defining different views? The choice is yours, but having this would make the app scale much better in the future and saves troubles of re-introducing this later.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Vuex&lt;/code&gt; - similar to the above point, depends on the complexity of the app. But because I tend to link applications to a real-time database so I decided to include this.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;CSS Pre-processors&lt;/code&gt; - I hate writing in CSS because it forces you to style the application in a repetitive manner. So I would always suggest to  turn this on unless the application requires no styling. E.g. if you are mostly using a components library&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Linter / Formatter&lt;/code&gt; - Linter helps to keep code consistent throughout the project, so I&apos;d always recommend this no matter the size of the project. In many cases the linter will be able to auto-fix linting errors, so including it has almost no drawbacks.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Unit Testing&lt;/code&gt; - unless the application is for demo only, otherwise I&apos;d strongly recommend having unit test in place.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;E2E Testing&lt;/code&gt; - similar to the point above, depends on the use case but strongly recommended.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;3. Use history mode for router?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/hR6GIBit5ytzxu7iTn87b/fc5372d18186482d9fc6cb9e56ceff31/Screenshot_2019-03-10_at_15.22.24.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.22.24&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I prefer history mode because it doesn&apos;t use the hash in the URL hack e.g. &lt;a href=&quot;http://domain.com/#/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;domain.com/#/&lt;/a&gt;. But be aware you may need to tweak some file path on production.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Pick a CSS pre-processor&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3i2nKUUxpPO9ekAAwHPwYJ/d8607268125e6611baa8740d7e46d360/Screenshot_2019-03-10_at_15.22.46.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.22.46&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I have gone with Stylus because it is pure node.js and doesn&apos;t require Ruby to run &lt;code class=&quot;language-text&quot;&gt;node-sass&lt;/code&gt; for compile &lt;code class=&quot;language-text&quot;&gt;SASS/SCSS&lt;/code&gt;. But this is just personal preference.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. Pick a linter / formatter config&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6rUCeyHvs9Xo5LnxS7RG2b/85380a2d3d8347635ee8958698f10d5a/Screenshot_2019-03-10_at_15.22.57.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.22.57&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I have gone with &quot;ESLint + Prettier&quot;, because I can install the Prettier plugin and it let take care of everything. I&apos;ve tried to go with something like &quot;ESLint + Airbnb config&quot; in the past. But the IDE/Text editor config always clashed with the ESLint config which can be annoying.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;6. Pick additional lint features&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6vNdVIIwRPYgimxykRMvC3/fdb9202044d19b8f1b958036e9c47688/Screenshot_2019-03-10_at_15.23.05.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.23.05&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I selected Lint on save here, but selecting the other choice or not selecting any will also work fine for me.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;7. Pick a unit testing solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/Gt42Mj0YkGERnQsRFsjkA/7571d44dcfc8a9117f1aab3c5988f0c9/Screenshot_2019-03-10_at_15.23.14.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.23.14&quot;&gt;&lt;/p&gt;
&lt;p&gt;  Jest wins here hands down, no competition. Having used both, I think Jest is better in every way.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;8. Pick a E2E tesing solution&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6qQQyDNDaeTRCMicQryRdG/d0305e40c349b2ebb724ca2f2cb02d8a/Screenshot_2019-03-10_at_15.23.23.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.23.23&quot;&gt;&lt;/p&gt;
&lt;p&gt;  Either of the two options is fine, but I&apos;ve gone with Cypress because I&apos;ve used it before.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;9. Where do you prefer placing config for Babel, PostCSS, ESLint, etc.?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3HdOpXhj3hsIsDTd14lfZ1/6a70aee48c6cd12156f144fdcf461776/Screenshot_2019-03-10_at_15.23.32.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.23.32&quot;&gt;&lt;/p&gt;
&lt;p&gt;  I&apos;d say always keep your configs separate. It is just simpler, cleaner and easier to find.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;10. Save this as a preset for future projects?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;  &lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/7DPu4e5KDkdhWQlHedBvQH/3541bf48a3392769b8e731495d9c81b4/Screenshot_2019-03-10_at_15.23.43.png&quot; alt=&quot;Screenshot 2019-03-10 at 15.23.43&quot;&gt;&lt;/p&gt;
&lt;p&gt;  This doesn&apos;t really matter, but I selected no.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;All Done&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Once the above steps are done, which will take a while the first time because it needs to pull down all the dependencies, you will be able to serve the application using &lt;code class=&quot;language-text&quot;&gt;npm run serve&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;The purpose of this blog post is to give an overview of the manual configs when using Vue CLI. I tried to keep everything simple without going into too many details. If you disagree or have any questions, feel free to get in touch in the comments section below or on hit me up on Twitter!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Which Node.js package manager tools to use?]]></title><description><![CDATA[<p>Everything in JavaScript world moves fast in comparison. Libraries and frameworks often fighting hard to dominate one another, in order to win the hearts of developers. Today let's discuss the positions of npm, yarn and a challenger called pnpm. Both yarn and pnpm were announced around the same time, only difference being Yarn was backed by Facebook and it gained traction much faster.</p>]]></description><link>https://blog.hao.dev/which-node-js-package-manager-tools-to-use</link><guid isPermaLink="false">https://blog.hao.dev/which-node-js-package-manager-tools-to-use</guid><pubDate>Tue, 29 Jan 2019 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Everything in JavaScript world moves fast in comparison. Libraries and frameworks often fighting hard to dominate one another, in order to win the hearts of developers. Today let&apos;s discuss the positions of npm, yarn and a challenger called pnpm. Both yarn and pnpm were announced around the same time, only difference being Yarn was backed by Facebook and it gained traction much faster.&lt;/p&gt;&lt;h2&gt;Performance&lt;/h2&gt;
&lt;p&gt;It is probably safe to say that &lt;code class=&quot;language-text&quot;&gt;pnpm &gt; yarn &gt; npm&lt;/code&gt; based on the stats released by &lt;a href=&quot;https://pnpm.js.org/docs/en/benchmark.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pnpm official site&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/gGLqTPZ8E43p76qH0NGcJ/aff23296e52cdd82f5366ffbe94d041b/react-app.svg&quot; alt=&quot;react-app&quot;&gt;&lt;/p&gt;
&lt;p&gt;Of course there could be biases here, but the source code for the benchmark was released on &lt;a href=&quot;https://github.com/pnpm/benchmarks-of-javascript-package-managers&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Github&lt;/a&gt; so anyone can go and take a look.&lt;/p&gt;
&lt;p&gt;If you are working on a personal project that has a lot of dependencies, and build time is painfully long. Then &lt;code class=&quot;language-text&quot;&gt;pnpm&lt;/code&gt; could be a valid choice here.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update (3rd Apr 2019)&lt;/strong&gt;: It seems like Yarn has also added the ability to do package security auditing, see &lt;a href=&quot;https://yarnpkg.com/lang/en/docs/cli/audit/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;offical doc&lt;/a&gt;. So there is even less reason to pick &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; over &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;Features&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;pnpm&lt;/code&gt; has been compared to &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; in these articles written by the author of &lt;code class=&quot;language-text&quot;&gt;pnpm&lt;/code&gt; , the articles are &lt;a href=&quot;https://medium.com/pnpm/pnpm-vs-yarn-monorepo-node-modules-6c025d50b9bd&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;pnpm vs Yarn: monorepo node_modules&lt;/a&gt; and &lt;a href=&quot;https://medium.com/pnpm/why-should-we-use-pnpm-75ca4bfe7d93&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Why should we use pnpm?&lt;/a&gt; respectively.&lt;/p&gt;
&lt;p&gt;It seems all three solutions offer similar key features, such as generating a lock file to ensure consistent dependency creation across different machines and environments. They are ultilise &lt;/p&gt;
&lt;h2&gt;Security&lt;/h2&gt;
&lt;p&gt;NPM security is a much bigger concern nowadays especially with the incidents such as &quot;event-stream@3.3.6 (Sep - November)&quot;. So I have seperated it out from the Features section, to discuss this on its own.&lt;/p&gt;
&lt;p&gt;There are currently two ways of getting notified of package vulnerabilities:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; has some basic checks baked into &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; (free)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://snyk.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Snyk&lt;/a&gt; is a open source tool that does what &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; does plus more (free only for open source)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; hasn&apos;t got the best performance, but it is the default node package manager (it&apos;s even in the name) used across platforms. For example, if you use &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; then Github can alert you when they detect vulnerabilities in our package dependencies. This feature is not avialable for &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;pnpm&lt;/code&gt;. One of the main reasons I&apos;d consider &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; would be the ability to quickly and easily audit dependencies.&lt;/p&gt;
&lt;p&gt;Of course the same could be achieved if Snyk is used somewhere in the pipeline, one might even argue this provides better audit than &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt;&apos;s solution. If, however, a project is private (not open source) then this would become more costly ($599+) depending on the team size. For a company that&apos;s able to make revenue off the product this might not be a very high cost, but for individual developers creating private personal projects this could be too much to consider. Then &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; would be the only viable solution.&lt;/p&gt;
&lt;p&gt;Maybe it is worth using &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; on the side of &lt;code class=&quot;language-text&quot;&gt;yarn&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;pnpm&lt;/code&gt; just for its &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; feature? It is worth mentioning the main reason to move to another package manager is for its speed improvements. Since &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; requires &lt;code class=&quot;language-text&quot;&gt;package-lock.json&lt;/code&gt; to function and building it is very time intensive. If &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; was added to a build pipeline purely for its security audit feature then the overall build time would be increased instead of decreased. At this point, it might be worth just taking a hit and say it is okay for the build to take 10 - 30s more, by getting some better security check in return.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;Out of these three options, my personal recommendation is to use &lt;code class=&quot;language-text&quot;&gt;npm&lt;/code&gt; . Not only because it comes with Node by default, which saves an additional step when you are setting up your machine but shortens CI pipeline builds. Since it is widely used and seen as the default package manager, so you do not get some of the benefits e.g. &lt;code class=&quot;language-text&quot;&gt;npm audit&lt;/code&gt; and Github&apos;s vulnerabilities alerts.&lt;/p&gt;
&lt;p&gt;When there are so many moving parts in the mordern frontend application already, it feels nice to not stress about another thing when developing software.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Let's embrace modern browsers in 2019]]></title><description><![CDATA[<p>Anyone who's been working with frontend web development should know the pain of browser support. I never understood why people stick with older browsers and I might never find the answer. I'm sure many fellow developers have the same confusion. Apart from corporate machines, there is very little reason to stay on older browser versions. Modern browsers are both more secure and faster! Need more reason? You get all that for free and there are plenty to choose from, what more can we ask for? For us, developers, nothing about this makes any sense. </p>]]></description><link>https://blog.hao.dev/let-s-embrace-modern-browsers-in-2019</link><guid isPermaLink="false">https://blog.hao.dev/let-s-embrace-modern-browsers-in-2019</guid><pubDate>Wed, 09 Jan 2019 23:07:00 GMT</pubDate><content:encoded>&lt;p&gt;Anyone who&apos;s been working with frontend web development should know the pain of browser support. I never understood why people stick with older browsers and I might never find the answer. I&apos;m sure many fellow developers have the same confusion. Apart from corporate machines, there is very little reason to stay on older browser versions. Modern browsers are both more secure and faster! Need more reason? You get all that for free and there are plenty to choose from, what more can we ask for? For us, developers, nothing about this makes any sense. &lt;/p&gt;&lt;p&gt;It is frustrating, but there is nothing we can do about it. Because our manager or Product Owner told us that we need to support &quot;that version of x browser&quot; (normally IE) because that is what the business wants. So here we go again, spending 20% of our effort developing a new feature then spend the other 80% making it work on browsers which even their own creator decided to drop support for a few years back.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5WtvK1FYcgiKyOsAeScEeY/6cbc9f85a5ed6f0f5ac098f116c8435c/what-if-i-told-you-we-dont-have-to-support-old-browsers.jpg&quot; alt=&quot;what-if-i-told-you-we-dont-have-to-support-old-browsers&quot;&gt;&lt;/p&gt;
&lt;p&gt;I know plenty of people have been pushing for this online for years, but this year is better than any to actually make a difference (how little it may be).&lt;/p&gt;
&lt;h2&gt;Better developer experience&lt;/h2&gt;
&lt;p&gt;Let&apos;s get the bias out of the way first. Yes, I&apos;m a developer, of course, I want to develop against fewer browsers and have an easy life. But how many people actually enjoy spending hours, days or even weeks debugging an issue on older browsers that may affect 0.1% of the customer?&lt;/p&gt;
&lt;p&gt;Even though this is a valid point, maybe don&apos;t go to your boss with the only explanation. Let&apos;s look at a few more reasons that don&apos;t sound like a selfish ass.&lt;/p&gt;
&lt;h2&gt;Build a better product&lt;/h2&gt;
&lt;p&gt;I believe in full-stack development, so plenty of people have tried to convince me why I should focus on one thing. When I go on explaining my reasons, they always come back with the following phrase. I think it works much better here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jack of all trades, master of none&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I have worked on projects where they wanted to support as many browsers as possible, sometimes up to 30 browsers &amp;#x26; devices combinations. It is easy to make that decision to say yes we will support everything. Let&apos;s put development and maintenance effort aside, and just imagine the amount of testing required to ensure the quality of those browsers/devices.&lt;/p&gt;
&lt;p&gt;I hear there are plenty of automation tools which can make it very easy to do these kinds of tests. There are, and I have used quite a few in the past. They are better than nothing, but they are not perfect either. Not only are UI tests a lot slower to run on the cloud, but they also only support a core list of browsers/devices.&lt;/p&gt;
&lt;p&gt;Before any production releases, some teams prefer to sanity check everything. Do you know how long it would take to manually sanity check 30 browser/device combinations? &lt;/p&gt;
&lt;p&gt;Spreading the team&apos;s effort this thin, and causing a lot of frustration in a lot of situations is plain unnecessary. If the business makes 50% of the revenue on IE10, then I say go ahead and support it. But sometimes it is worth taking a look at the big picture. Spending 10% or even 20% of the resource to support 1% of the customer is not a rare thing business decides to do. If this is happening in your team, it is worth bringing this up and at least discuss it.&lt;/p&gt;
&lt;p&gt;On the other side, now imagine all you have to do is support 5 browsers (latest versions). The team would have so much more focus to build a better product. Instead of having a backlog full of bugs on different browsers, and creating a half-baked product. Wouldn&apos;t it feel better to see a backlog of features and no bugs?&lt;/p&gt;
&lt;h2&gt;Honestly it is time&lt;/h2&gt;
&lt;p&gt;Most applications written nowadays are still converted and polyfill to ES5 standard. Do you know when ES5 spec was released? 2009! That is 10 years ago!&lt;/p&gt;
&lt;p&gt;Okay, I need to calm down. I&apos;m not saying today onwards everyone should be publishing app written in ES2018, that is a tad extreme. But is ES2015 (ES6) really that much to ask? Don&apos;t trust me when I say it is fine to write and publish apps in ES2015? Let&apos;s look at the facts!&lt;/p&gt;
&lt;p&gt;Check out this &lt;a href=&quot;https://kangax.github.io/compat-table/es6/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;awesome tool&lt;/a&gt; created by kangax. It shows a full breakdown of the supported of different browsers. Let&apos;s assume that our intended user is using the modern browsers, the lowest support % is Edge at 96%. &lt;/p&gt;
&lt;p&gt;Yes, we are not at 100% yet. You really want to use those missing features, then release your code in ES6 and polyfill that 4 % of ES2015 features. I personally think it is just easier not to use those features, but hey, the choice is yours.&lt;/p&gt;
&lt;h2&gt;Handle old browser visits&lt;/h2&gt;
&lt;p&gt;What if a user on an older browser comes to my site? Would they just see a broken page? Surely that can&apos;t be good, there&apos;s a brand name to live up to! Good, let&apos;s see what we can do here.&lt;/p&gt;
&lt;p&gt;There is this site called &lt;a href=&quot;http://outdatedbrowser.com/en&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;outdatedbrowser.com&lt;/a&gt;. Go and take a look, it is a beautiful site. This is free, responsive and available in many languages. It only appears when people browse your site on the older browser and tell the user that they should consider download one of the listed modern browsers to continue using your site.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2N15VHC0tayoC422Ma6oeE/b3b91a09d452fb730d3c79f6f151942d/browser_modern.PNG&quot; alt=&quot;browser modern&quot;&gt;&lt;/p&gt;
&lt;p&gt;With this as a fallback, your modern browser users will experience a faster site and the older browser users will be greeted with a nice looking fallback site as well. I think the first step towards a more modern web is to have as many sites showing this as possible. When a user keeps seeing this page, they will understand how fast behind they are and will want to download one of those nice looking modern browsers. If not, then sooner or later they will be forced to if they want to see the content on these sites.&lt;/p&gt;
&lt;p&gt;If we keep tailoring for the small percentage of people, we might be still using ES5 after another 10 years. For some people, some tough love is needed. But remember, by upgrading they are also more secure and better protected online as well.&lt;/p&gt;
&lt;h2&gt;Fight for a better web&lt;/h2&gt;
&lt;p&gt;I also think the first thing any developer should say after seeing the list of supported browsers (and maybe devices) is &quot;can I see the analytics of the site which decided this list&quot;? Yes, analytics is key. It might not be 100% accurate, but it should be the first thing to look at when considering to support older browsers. As developers, people should be coming to us and try to convince us if a browser is needed e.g. over 10,000 users use it to visit our site. It shouldn&apos;t be us convincing the business. But since we don&apos;t live in a perfect world, at least ask the above question. It is not just making your own life easier, it is also saving the business tones of resources which we will get to later.&lt;/p&gt;
&lt;p&gt;After you&apos;ve seen the latest analytics numbers, the next step to make some the tough decisions or try to convince some difficult people in the business. Explain to them the different benefits and fallbacks that can be put in place to handle everything gracefully even if someone&apos;s grandparents load the site on IE6. Yes, the &lt;a href=&quot;http://outdatedbrowser.com/en&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;outdatedbrowser.com&lt;/a&gt; tool works on IE6! How many other sites out there works on browsers that old? With this tool, you are technically improving the customer experience for people with old browsers (say there is someone crazy enough to be using IE6 visiting your site).&lt;/p&gt;
&lt;h2&gt;A faster web&lt;/h2&gt;
&lt;p&gt;We all know small JavaScript bundles are good, it means the site will load faster on low-end devices. ES2015 lets us write much better code to achieve the same things in ES5, there are also plenty of new features of ES2015 which doesn&apos;t exist in ES5 which is why we add in those huge polyfills.&lt;/p&gt;
&lt;p&gt;But how much small is ES2015 bundle compared to ES5?&lt;/p&gt;
&lt;p&gt;This is a tricky question to answer because it honestly depends. But just as an example, let&apos;s take a look at the source code of two implementations of &lt;a href=&quot;http://todomvc.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;todomvc&lt;/a&gt;. One is written in ES2015 and the other in ES5.&lt;/p&gt;
&lt;p&gt;Things to note:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It was not implemented by the same person, so the code quality may vary&lt;/li&gt;
&lt;li&gt;This is just for fun, don&apos;t quote me based on this example&lt;/li&gt;
&lt;li&gt;Try doing a comparison on the project you are working on, it will be much more relevant than this example&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I combined all the source code of the &lt;a href=&quot;https://github.com/tastejs/todomvc/tree/gh-pages/examples/vanilla-es6&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ES2015 vanilla&lt;/a&gt; and then minified it, the uncompressed size is 12,074 bytes, gzip compressed size is 3,190 bytes.&lt;/p&gt;
&lt;p&gt;I then did the same for &lt;a href=&quot;https://github.com/tastejs/todomvc/tree/gh-pages/examples/vanillajs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;ES5 vanilla&lt;/a&gt; and the uncompressed size is 16,085 bytes, gzip compressed size is 4,104 bytes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6MogdZ9TDqAiuAmGgG4mSW/92decae4e13a868a32884414dff90ffb/todomvc_bundle_comparison.PNG&quot; alt=&quot;todomvc bundle comparison&quot;&gt;&lt;/p&gt;
&lt;p&gt;The actual difference here is pretty tiny especially after gzip, but the percentage difference (ES2015 is about 75% smaller than ES5) is nice to see. It is worth remembering this is only several hundred lines of code. In a big problem, 25% of bundle size is quite considerable. Especially considering todomvc doesn&apos;t use any polyfills, if you use a few big polyfills then that&apos;s going to add a lot more to the bundle size.&lt;/p&gt;
&lt;h2&gt;We are professionals&lt;/h2&gt;
&lt;p&gt;If you are walking past a stranger and see their bag is open, wouldn&apos;t you tap them on the shoulder and tell them? You are all nice people, of course, you would. Now, you see your company wasting time and effort on some poor decisions made a while back, the solution is simple and makes everyone&apos;s life easier. Should you not raise your hand and make everyone aware of the issue?&lt;/p&gt;
&lt;p&gt;Often the people making the decisions doesn&apos;t know they are wasting resources on things that don&apos;t matter. As professionals, we should stand our ground and say what we think is right. Maybe do it in a polite and assertive way, so people don&apos;t think you are crazy or you are attacking them personally. I&apos;m sure most bosses would love to cut 20% cost and give up 1% of the customers, it is simple maths. But remember we are not &quot;giving up&quot; on those customers either, we have discussed fallbacks already which will help them to &quot;find their way&quot;.&lt;/p&gt;
&lt;p&gt;As professionals, we should and I&apos;m sure most of us want to create the best product. It is also a much better ego boost when people say &quot;oh I love your product!&quot; compared to &quot;can you fix x, y and z issues on your site? I keep getting them&quot;.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;I hope the above is enough to convince you that this is something that is worth doing, not just to make our developers&apos; lives easier. It will also benefit our companies and our customers. But if you can&apos;t get this point across to anyone at your workplace no matter how hard you tried. Then at least do this on your personal sites :)&lt;/p&gt;
&lt;p&gt;I hope 2019 is the year ES2015 will become the default standard when creating new projects, and that ES5 polyfill becomes a thing in the past!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Adding code syntax highlights to gatsby blog]]></title><description><![CDATA[<p>I was recently learning about Mobx and came across a blog with nice code syntax highlighting, and thought the code snippets on my blog are both hard to read and pretty boring to look at. So it inspired me to upgrade my blog!</p>]]></description><link>https://blog.hao.dev/adding-code-syntax-highlights-to-gatsby-blog</link><guid isPermaLink="false">https://blog.hao.dev/adding-code-syntax-highlights-to-gatsby-blog</guid><pubDate>Mon, 24 Dec 2018 16:52:00 GMT</pubDate><content:encoded>&lt;p&gt;I was recently learning about Mobx and came across a blog with nice code syntax highlighting, and thought the code snippets on my blog are both hard to read and pretty boring to look at. So it inspired me to upgrade my blog!&lt;/p&gt;&lt;p&gt;Here&apos;s the &lt;a href=&quot;https://www.robinwieruch.de/redux-mobx-confusion/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;blog post&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;A quick comparison&lt;/h2&gt;
&lt;h3&gt;The blog I saw&lt;/h3&gt;
&lt;p&gt;The blog post is a very nice read, but what caught my eye and made me save it into my notes is the awesome looking code syntax highlighting on this site. I&apos;ve attached a screenshot below.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/22gTH1FwnG6UuSyaaGKCAa/1e60c4fa100f83371a57b00c7642fcd8/Screenshot_2018-12-24_at_10.38.33.png&quot; alt=&quot;External Blog Code Snippet&quot;&gt;&lt;/p&gt;
&lt;p&gt;Code syntax highlighting is of course not a new thing, it is seen on a lot of blog site. Some even go as far as including the jsfiddle (or equivalent) code snippet which lets users click through and run them when reading the content.&lt;/p&gt;
&lt;p&gt;For me, the colours really caught my eye. The bold and standout colour makes identifying code and normal text that much easier. I also really like the green padding on the left of the code snippet.&lt;/p&gt;
&lt;h3&gt;My blog - Before&lt;/h3&gt;
&lt;p&gt;Just as a reference, this is how my blog looked before I made any changes.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5YWnZvTa2AMoeuuiuSiQIk/d12a9946d84b0224f55f4b8025a6eefc/Screenshot_2018-12-24_at_09.53.35.png&quot; alt=&quot;Old blog code snippet&quot;&gt;&lt;/p&gt;
&lt;p&gt;The theme was based off &lt;a href=&quot;https://github.com/pagecho&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Cho&lt;/a&gt;&apos;s work. It heavily uses different shades of grey, which meant the site looked clean and minimalistic, but code in the text is not so easy to read. &lt;/p&gt;
&lt;h3&gt;My blog - After&lt;/h3&gt;
&lt;p&gt;After some tweaking, I came up with this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/cIo45hoeHemy8uCCSIUw6/b3faa7d098632dd97e207bf6d9efee9a/Screenshot_2018-12-24_at_09.53.12.png&quot; alt=&quot;New blog code snippet&quot;&gt;&lt;/p&gt;
&lt;p&gt;The result is a combination of the blog I mentioned above, &lt;a href=&quot;https://prismjs.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Prism&lt;/a&gt;&apos;s &quot;tomorrow&quot; theme and some of my own custom modifications. It definitely made everything pop a bit more, but it could be a bit too much. I&apos;ll test it out for a while, and may come back and dial it down later.&lt;/p&gt;
&lt;h2&gt;How it was done&lt;/h2&gt;
&lt;p&gt;Using &lt;code class=&quot;language-text&quot;&gt;gatsby&lt;/code&gt; the whole process is pretty simple, with a few caveats.&lt;/p&gt;
&lt;p&gt;I mainly used &lt;a href=&quot;https://www.gatsbyjs.org/packages/gatsby-remark-prismjs&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;gatsby-remark-prismjs&lt;/a&gt; plugin to do all of this. I considered of integrating everything myself manually. But I think it would lead to a messier codebase. So at the end, I decided to stick with this plugin.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;This is quite straightforward:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;npm install --save gatsby-transformer-remark gatsby-remark-prismjs prismjs&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then just add the following plugin config into the Gatsby config:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// gatsby-config.js&lt;/span&gt;

&lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;gatsby-transformer-remark&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;resolve&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;gatsby-remark-prismjs&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token literal-property property&quot;&gt;options&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;classPrefix&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;language-&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;inlineCodeMarker&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;aliases&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;showLineNumbers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;noInlineHighlight&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Understand the configs&lt;/h3&gt;
&lt;p&gt;First of all, let&apos;s run through a quick explanation of the above configs which wasn&apos;t clearly explained by the documentation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;classPrefix&lt;/code&gt; will default to &quot;language-text&quot; if not set which doesn&apos;t add any code syntax highlighting, so just leave as it is&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;inlineCodeMarker&lt;/code&gt; still not sure what this does, but you can still use the inline code with single backticks&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;aliases&lt;/code&gt; in case you don&apos;t want to keep writing long language names e.g. &quot;js&quot; instead of &quot;javascript&quot;&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;showLineNumbers&lt;/code&gt; I recommend keeping this to false. I tried setting this global setting to TRUE, then override it to FALSE in specific situations. But it did not work. So it seems it is only possible to override to TRUE, for now, so leave this as FALSE unless you want line numbers in every code snippet.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;noInlineHighlight&lt;/code&gt; This allows you to highlight certain lines in a code snippet which might be useful, I have turned this on thinking I may use it in the future. Note: this will conflict with &lt;code class=&quot;language-text&quot;&gt;showLineNumbers&lt;/code&gt; in terms of CSS styles, so if you want to use this, make sure CSS works in both situations.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Where to include the CSS&lt;/h3&gt;
&lt;p&gt;The documentation wasn&apos;t too clear on this, I tried to add Prism theme into &lt;code class=&quot;language-text&quot;&gt;gatsby-browser.js&lt;/code&gt; but it did nothing for me. The file I needed was in fact &lt;code class=&quot;language-text&quot;&gt;src/layouts/index.js&lt;/code&gt;, after importing &lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prismjs/themes/prism-tomorrow.css&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;at the top code syntax highlighting was showing up correctly.&lt;/p&gt;
&lt;p&gt;If you wish to use line numbers then you&apos;ll need to import the following CSS file as well.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;prismjs/plugins/line-numbers/prism-line-numbers.css&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Wait, we are not done just yet. If you want to use &lt;code class=&quot;language-text&quot;&gt;showLineNumbers&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;noInlineHighlight&lt;/code&gt; then you&apos;ll have to include a custom CSS file which holds the following overrides as well. For me, since I&apos;m using Prism&apos;s &quot;tomorrow&quot; theme, I&apos;ve included the following (depending on your theme you probably want to change this) and I&apos;ve also decided to use SCSS here.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt;  &lt;span class=&quot;token string&quot;&gt;&apos;../styles/prism-override.scss&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;scss&quot;&gt;&lt;pre class=&quot;language-scss&quot;&gt;&lt;code class=&quot;language-scss&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/**
* Add back the container background-color, border-radius, padding, margin
* and overflow that we removed from &amp;lt;pre&gt;.
*/&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.gatsby-highlight &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #2d2d2d&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-radius&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.3em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5em 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; auto&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;border-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.5em solid #FF9800&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;/**
  * Remove the default PrismJS theme background-color, border-radius, margin,
  * padding and overflow.
  * 1. Make the element just wide enough to fit its content.
  * 2. Always fill the visible space in .gatsby-highlight.
  * 3. Adjust the position of the line numbers
  */&lt;/span&gt;
  &lt;span class=&quot;token selector&quot;&gt;pre[class*=&quot;language-&quot;] &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; transparent&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;margin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;padding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;overflow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; initial&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;float&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; left&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;min-width&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;100% &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; 3em&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token selector&quot;&gt;&lt;span class=&quot;token parent important&quot;&gt;&amp;amp;&lt;/span&gt;.line-numbers &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 3em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

      &lt;span class=&quot;token selector&quot;&gt;.line-numbers-rows &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;calc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;100% &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; 25px&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unset &lt;span class=&quot;token important&quot;&gt;!important&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

      &lt;span class=&quot;token selector&quot;&gt;.gatsby-highlight-code-line &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;background-color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; #455A64&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; block&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;margin-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;margin-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; -1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;padding-right&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 1em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;padding-left&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 0.75em&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[A quick Parcel bundler review]]></title><description><![CDATA[<p>I heard about Parcel about a year ago, back then I didn't feel like it was stable enough to be used in my personal projects. And that I was just getting into RollupJS, compared to Webpack that was already a massive step up in terms of ease of use and simplified configuration. A year later, now I am finally ready to try out Parcel and see exactly how simple it is compared to the established alternatives like RollupJS and Webpack.</p>]]></description><link>https://blog.hao.dev/a-quick-parcel-bundler-review</link><guid isPermaLink="false">https://blog.hao.dev/a-quick-parcel-bundler-review</guid><pubDate>Tue, 04 Dec 2018 19:20:00 GMT</pubDate><content:encoded>&lt;p&gt;I heard about Parcel about a year ago, back then I didn&apos;t feel like it was stable enough to be used in my personal projects. And that I was just getting into RollupJS, compared to Webpack that was already a massive step up in terms of ease of use and simplified configuration. A year later, now I am finally ready to try out Parcel and see exactly how simple it is compared to the established alternatives like RollupJS and Webpack.&lt;/p&gt;&lt;h2&gt;My Experience With Parcel&lt;/h2&gt;
&lt;p&gt;Parcel is one of the newest contenders in this field, it aims to shake off all the dead weight that comes with a bundler and hopes to enable developers to jump on a new personal project with ease. For me personally, it is stressful to spend a couple of hours or even half a day configuring bundler and build tool when I think of a new personal project. Yes, I could just do it once and then copy and paste a generic setup. But it means using out-dated dependencies, have generic code that does nothing and inefficient setup, to me that sounds like a horrible idea and I wouldn&apos;t want to start a greenfield project on top of a brownfield setup. So when I created the &lt;a href=&quot;https://github.com/hdong92/software-developer-career-roadmap&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;software-developer-career-roadmap&lt;/a&gt; project, knowing it will be a simple Single Page Application (SPA) I decided to try out Parcel.&lt;/p&gt;
&lt;h3&gt;Set up&lt;/h3&gt;
&lt;p&gt;The initial experience was amazing, everything was set up and ready to go with one command &lt;code class=&quot;language-text&quot;&gt;npm install parcel-bundler typescript shiva&lt;/code&gt;. Of course, only parcel-bundler is relevant to this blog.&lt;/p&gt;
&lt;h3&gt;Hello World&lt;/h3&gt;
&lt;p&gt;The next step is to add an &lt;code class=&quot;language-text&quot;&gt;index.html&lt;/code&gt; file and an &lt;code class=&quot;language-text&quot;&gt;app.js&lt;/code&gt; file just so we have something to serve. Feel free to take a look at the repository mentioned above, but I&apos;m going to add an example here too just to keep things simple.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;// index.html

&lt;span class=&quot;token doctype&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;!&lt;/span&gt;&lt;span class=&quot;token doctype-tag&quot;&gt;DOCTYPE&lt;/span&gt; &lt;span class=&quot;token name&quot;&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;html&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lang&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;en&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;charset&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;UTF-8&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;viewport&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;width=device-width, initial-scale=1.0&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;meta&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;http-equiv&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;X-UA-Compatible&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;content&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;ie=edge&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;head&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./app.js&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;html&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// app.js&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; helloWorld &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createTextNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;hello world&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;body&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;appendChild&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;helloWorld&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Serve&lt;/h3&gt;
&lt;p&gt;With Parcel it is super easy to serve the simple web app we just created, simply run &lt;code class=&quot;language-text&quot;&gt;parcel serve src/index.html&lt;/code&gt; or alternatively you could also consider using &lt;code class=&quot;language-text&quot;&gt;parcel watch&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;parcel build&lt;/code&gt; depending on the desired outcome.&lt;/p&gt;
&lt;h3&gt;Challenges&lt;/h3&gt;
&lt;p&gt;During my first few hours with Parcel I encountered 2 issues, which could be small or massive depending on what kind of projects you are working on.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;I couldn&apos;t get Hot Module Reload working. The official site mentioned that Parcel has Hot Module Reload built-in, however, I wasn&apos;t able to get it working at all. In fact, I couldn&apos;t even get the browser to reload on file change. To see the changes I&apos;ve made, I always had to refresh on the browser which did get annoying after a while.&lt;/li&gt;
&lt;li&gt;No easy way to use a JSON file as a mocked service call. For the SPA that I was working on, I didn&apos;t want or need to create a real backend so I decided to mock it by requesting a local JSON file directly. This way I can have the code logic to behave as if a proper API call was made, but still, keep the data with the repo (where I want other people&apos;s contribution in as well). The challenge here is that when I serve the app, a &lt;code class=&quot;language-text&quot;&gt;dist&lt;/code&gt; folder gets generated and all the files within it created from the source code. But I didn&apos;t have an easy to move the JSON file into dist folder. I tried a few different methods e.g. include JSON in HTML and etc, but nothing worked well enough. In the end, I didn&apos;t have a choice but to change the JSON file into a normal JavaScript object and remove the fake API call entirely.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;Going forward, I&apos;m definitely going to be using Parcel as much as I can. But where does this leave Webpack, RollupJS and other bundle tools?First of all, stop using other bundle tools in new projects right now! Not that they are not good, but Webpack, RollupJS and Parcel are just that much better in their own unique ways.&lt;/p&gt;
&lt;p&gt;Webpack is really good for an enterprise application that is complex, requires a high level of customisation and aiming to scale. There&apos;s a massive community behind it, with popular projects like VueJS and React all utilising it in their starter projects. There are a countless number of Webpack plugins to pick from, if there&apos;s something you want to do, the chances are there are multiple plugins to pick from. Another benefit is that, since there are so many people using and creating plugins for Webpack, you can be assured there are plenty of resources online.&lt;/p&gt;
&lt;p&gt;RollupJS is still a solid choice in my opinion, mostly used for creating libraries instead of an application. &quot;Tree-shaking&quot; was one of its main competitive advantages when it was introduced, at that time Webpack did not support this feature or it was very difficult to achieve even if it was possible. Rollup is still very simple and easy to use compared to Webpack, but still, have the right amount of packages to support it. When creating a library the most important things are tree-shaking, code splitting, minification and creating code bundles. By allowing multiple entry points, and utilising its plugins Rollup can all of those things with ease. Webpack is a bit of an overkill, in this case, adding additional unnecessary complexity. Whereas, Parcel lacks some of those plugins to achieve the same outcome.&lt;/p&gt;
&lt;p&gt;Parcel out of the box requires no setup, and I mean no setup. There is no need to create configuration files for each environment, not even a single line of configuration. But at the same time, it only will let you do the basics, and there isn&apos;t a long list of plugins as of today. My experience with it is that it does the core part of bundler&apos;s job very well. Getting started with a new project cannot be easier. However, as soon as you need more customisation (the one I explained above) you&apos;ll have doubts about choosing it over Webpack or Rollup. I would definitely keep using Parcel, it is amazing at what it intends to do. I would happily live with the challenges it brings then to add 5 - 10 additional NPM dependencies and create a build process that is both slow and complex. My recommendation is to only use Parcel is simple or small projects, there&apos;s a high chance of migrating to another bundler tool if you do decide to use Parcel on a medium or large project.&lt;/p&gt;
&lt;h2&gt;Additional notes&lt;/h2&gt;
&lt;p&gt;Parcel usage is still tiny compared to the alternatives listed in this blog. Check out the trends on NPM: &lt;a href=&quot;https://www.npmtrends.com/parcel-bundler-vs-webpack-vs-rollup&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.npmtrends.com/parcel-bundler-vs-webpack-vs-rollup&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Or if you are too lazy to click here are the diagrams:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5VtnhbtWb68KeWK8O6mSoQ/da18110b5a00d1f6c1da3a0ea944281c/Screenshot_2018-12-04_at_11.55.47.png&quot; alt=&quot;Screenshot 2018-12-04 at 11.55.47&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/40oJGPHxLGeI4QIOI8mIw/8605475e3402488f73011fccc52ca990/Screenshot_2018-12-04_at_11.56.08.png&quot; alt=&quot;Screenshot 2018-12-04 at 11.56.08&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Improving CSS performance of Cordova apps on Android TVs]]></title><description><![CDATA[<p>I have recently been working in a team that creates web apps that sits inside a webview wrapper application on Android TVs. Coming from more of a web background, even though this is still using React and other common web technologies, there are still some interesting challenges.</p>
<p>If you have read the title of this blog, then you know we'll be talking about CSS performances on Android TVs. This is not a new topic, since performance is an issue on all low end spec devices, but the ones comes with TV are a bit different.</p>]]></description><link>https://blog.hao.dev/improving-css-performance-of-cordova-apps-on-android-tvs</link><guid isPermaLink="false">https://blog.hao.dev/improving-css-performance-of-cordova-apps-on-android-tvs</guid><pubDate>Sat, 17 Nov 2018 20:16:00 GMT</pubDate><content:encoded>&lt;p&gt;I have recently been working in a team that creates web apps that sits inside a webview wrapper application on Android TVs. Coming from more of a web background, even though this is still using React and other common web technologies, there are still some interesting challenges.&lt;/p&gt;
&lt;p&gt;If you have read the title of this blog, then you know we&apos;ll be talking about CSS performances on Android TVs. This is not a new topic, since performance is an issue on all low end spec devices, but the ones comes with TV are a bit different.&lt;/p&gt;&lt;h2&gt;App architecture &amp;#x26; Webview&lt;/h2&gt;
&lt;p&gt;To understand the problem, let&apos;s first take a look at how everything what we are dealing with. Essencially, there is a shell app which was created using Cordova which loads the web application into the view. Cordova, if you are new to it, connects up JavaScript events to native Android events and sensors. This means with Cordova it is now possible to detect things like &quot;if the device has internet connection and etc&quot;.&lt;/p&gt;
&lt;p&gt;We are not too concerned with the Cordova shell app, but it is worth knowing it uses whatever version of &lt;a href=&quot;//developer.android.com/reference/android/webkit/WebView&quot;&gt;Webview&lt;/a&gt; that comes with the OS. Different versions of Android has different version of Webview, which makes sense since Android will just include the latest Webview when a new version of Android is released. &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since Android 4.4 (KitKat), the WebView component is based on the Chromium open source project. WebViews now include an updated version of the V8 JavaScript engine and support for modern web standards previously missing in old WebViews. New Webviews also share the same rendering engine as Chrome for Android, so rendering should be much more consistent between the WebView and Chrome.
In Android 5.0 (Lollipop), the WebView has moved to an APK so it can be updated seperately to the Android platform. To see what version of Chrome is currently used on a Lollipop device, simply go to Settings &amp;#x3C; Apps &amp;#x3C; Android System WebView and look at the version. -- Chrome Team&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Based on the above description by the Chrome team, this means the user could actually update the version of Webview. But in reality it is much safer to build for the default version of Webview since not many users won&apos;t be updating to newer versions. Luckily, all the TVs my team support are Android 5.0 (Lollipop) or later. &lt;/p&gt;
&lt;p&gt;So why am I talking about Webviews, isn&apos;t this a blog on CSS performance? To understand the root problem and the solution, it is important to understand how Webview fits into all this. Next we&apos;ll be talking about the actual issue and solutions, and hopefully this will make more sense by then.&lt;/p&gt;
&lt;h2&gt;What is the problem?&lt;/h2&gt;
&lt;p&gt;I was working on a new feature which allowed user to browse and find the right content by using a grid navigation. The grid will scroll vertically depending on if the user wishes to see more content (no horizontal scroll). When the user tries to scroll to a row that is outside of the current viewport, there is a nice shifting transition that brings all the tiles upwards or downwards. Then the focus maintains on the new tile and the new row will be within the viewport.&lt;/p&gt;
&lt;p&gt;After the implementation has been done, the feature was tested on one of the highest spec TVs and I noticed some weird behaviors.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Verticle navigation is way more jumpy and laggier than horizontal navigation&lt;/li&gt;
&lt;li&gt;This issue seems to be only occuring on the TVs, on our development machines (Mac) everything works fine&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;It didn&apos;t take long for me to conclude that this is a performance issue.&lt;/p&gt;
&lt;h2&gt;The solutions&lt;/h2&gt;
&lt;p&gt;After some research and investigation using the Chrome browser tools, I couldn&apos;t find anything suspicious. However, I came across some discussions around how some people have tried to trigger GPU rendering instead of CPU for better CSS animation performance.&lt;/p&gt;
&lt;p&gt;There are two ways of achieving this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function/translateZ&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;TranslateZ(0)&lt;/a&gt; hack: It is normally used in CSS to translate a component on the Z-axies as the name suggests. A less well-known functionality is that even by declaring 0 translate (no change) it will force hardware acceleration and thus render with the GPU instead of CPU.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/will-change&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;will-change&lt;/a&gt;: prepares the browser to complete optimizations before an element is actually changed. As a result, the transition will appear much smoother to the end user even on low end devices.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;After testing both of the ideas above, everything looked the way I expected. So which of the two solutions should we pick? The normal thinking might be that since &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; is designed for this and &lt;code class=&quot;language-text&quot;&gt;TranslateZ()&lt;/code&gt; is a hack, it only makes sense to use &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; right?&lt;/p&gt;
&lt;p&gt;Well, I actually went with &lt;code class=&quot;language-text&quot;&gt;TranslateZ()&lt;/code&gt; due to the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; too many times in the code will actually cause enough overhead to decrease the performance of the web app. The recommended approach is to add &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; on the element right before the animation, then take it off the DOM again. Which requires a lot of additional logic in place to add and remove it from the elements, which in this case there quite a few. &lt;/li&gt;
&lt;li&gt;The other reason being &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; was released not too long ago, so the support is a bit lacking in older browser version. If there happens to be a TV with older Webview then this optimisation apply.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is worth to point out that over using &lt;code class=&quot;language-text&quot;&gt;TranslateZ()&lt;/code&gt; would also lead to performance issues. Each time it is used, it will move the applied component to its own composited layer. So even if you do decide to use this hack, try to keep it at a minimum. Overall, based on my research I felt that this would be less of an issue compared to &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt;. &lt;/p&gt;
&lt;p&gt;To learn more about &lt;code class=&quot;language-text&quot;&gt;will-change&lt;/code&gt; checkout &lt;a href=&quot;https://dev.opera.com/articles/css-will-change-property/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;css will-change property&lt;/a&gt; by Opera and &lt;a href=&quot;https://developers.google.com/web/updates/2016/09/re-rastering-composite&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;re-rastering composite layer&lt;/a&gt; by Google.&lt;/p&gt;
&lt;p&gt;BUT WAIT! What about the fact this issue only occured on TVs and not on the development machines? My conclusion is that newer version of Chrome/Chromium must be doing some kind of optimisation by default for 2D translates, because when I checked the Mac&apos;s Chrome dev tools (without the above fix) the GPU was already being used to do the rendering for those transitions. This explains why I never saw the issue on Mac and the TVs had this issue because they were on lower version of Webview that didn&apos;t have this optmisation built-in.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;The &lt;code class=&quot;language-text&quot;&gt;TranslateZ()&lt;/code&gt; hack is a good way of improving CSS performance for sites with a lot of transitions. However, since hardware acceleration is forced and GPU render drains more battery, one thing to bear in mind is that we may want to avoid doing this on mobile devices or anything that runes on battery (e.g. unplugged laptops). For example, in the future we could use &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Navigator/getBattery&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;navigator.getBattery()&lt;/a&gt; and user-agent to detect the user&apos;s device and bettery state. We may then wish to disable transitions to best optimise for mobile browsing experience. Obviously, this is not a concern for plugged in devices and TVs which are plugged in all the time.&lt;/p&gt;
&lt;p&gt;There are a few other things I&apos;d like to mention:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;I joined this project late after everything was already setup. So I am not sure why React-native or other ways of developing for cross-platform wasn&apos;t picked instead&lt;/li&gt;
&lt;li&gt;Not all smart TVs are based off Android, so the performance issue in some TVs could be a lot worse than what I explained (and may not even apply). On the other hand, not all systems demand as much resources as Android does. So your mileage may vary.&lt;/li&gt;
&lt;li&gt;Amazon&apos;s FireTV stick is also based on Android, and with the FireTV 4k&apos;s recent release, the market share of Android TVs is very likely to further increase.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Triad Learning: a new way to train and learn]]></title><description><![CDATA[<p>This is my fifth year as a software developer, it certainly has been a long and fulfilling learning journey until now. There are simply too much to learn no matter how we plan it out, not even mentioning there are new tech coming out every day. When I started my career I felt like I knew so little about the world of software development, and that feeling never really went away. This game of catch-up is tiring to say the least, some even gave this overwhelming feeling a name - development fatigue.</p>]]></description><link>https://blog.hao.dev/triad-learning-a-new-way-to-train-and-learn</link><guid isPermaLink="false">https://blog.hao.dev/triad-learning-a-new-way-to-train-and-learn</guid><pubDate>Wed, 22 Aug 2018 09:08:00 GMT</pubDate><content:encoded>&lt;p&gt;This is my fifth year as a software developer, it certainly has been a long and fulfilling learning journey until now. There are simply too much to learn no matter how we plan it out, not even mentioning there are new tech coming out every day. When I started my career I felt like I knew so little about the world of software development, and that feeling never really went away. This game of catch-up is tiring to say the least, some even gave this overwhelming feeling a name - development fatigue.&lt;/p&gt;&lt;h2&gt;The struggle is real&lt;/h2&gt;
&lt;p&gt;When new joiners ask me what&apos;s the best way to learn something, I had often struggled to find a good way to explain my way of learning. I want to tell them to learn everything, because everything is connected on some levels in the world of software development. To be a good developer, it takes a lot more than knowing  a framework (e.g. React) very well. Other questions comes to mind too: &quot;how did you structure your project and why?&quot;, &quot;which build tool did you use, how do you plan to publish the code?&quot;, &quot;have you considered the other tool/lib/framework?&quot;, &quot;what is your testing strategy?&quot; and etc. All of these are common and practical questions people ask when they join a project, a good developer should be able to explain them. But it takes a lot more than reading a few article to truly grasp answers for these questions. So I often default to saying &quot;depending on what you are trying to do...&quot;.&lt;/p&gt;
&lt;p&gt;I personally also struggled a lot in this area, because I&apos;m interested in a lot of areas: frontend, backend, mobile, and etc. It becomes increasingly difficult to keep track of everything as I learn more. Knowledge, like a spider web, the area increase exponentially as it grows. When I talk to people about this, the more senior developers often say that I should pick one area and focus on it. And they would go on to quote the following line:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Jack of all trades, master of none&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On some levels I do agree with it, I only have finite amount of time like everyone else. So even if I work twice as hard to learn I still won&apos;t be able to learn everything I want. But at the same time, I don&apos;t just want to give up. I have experienced the benefits of full stack, the ability to see a problem from different perspective and being able to find a solution that make sense overall.&lt;/p&gt;
&lt;p&gt;So what then?&lt;/p&gt;
&lt;p&gt;I wanted to find a way of learning that is practical and easy to follow. It needs to be something that can be followed by an individual as well as adopted by multi people team.&lt;/p&gt;
&lt;h2&gt;Path to the solution&lt;/h2&gt;
&lt;p&gt;I analysed my situation and what is going on at my work place. Unsurprisingly, I found quite a lot of problem. &lt;/p&gt;
&lt;p&gt;I decided to first write down my way of learning to see where the problem are:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Read articles and find out about something new.&lt;/li&gt;
&lt;li&gt;Keep track of it, so can back to it at a later point.&lt;/li&gt;
&lt;li&gt;If something came up a few times, I then spend more time doing some quick research on that topic. This could be reading the official documentation, and checking out a few blog posts online. I don&apos;t spend too much time at this point, the aim is to understand the high level gist.&lt;/li&gt;
&lt;li&gt;If I find it interest and I have the opportunity, then I go on to get some hands on experience through personal project or other means.&lt;/li&gt;
&lt;li&gt;If I get stuck I&apos;d google for some tutorials in that specific area&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Surprisingly enough, this process is actually okay I think. The area that I struggle the most with, turns out to be keeping track of things to learn and how to best management them. I had done an exercise to tackle that separately (I&apos;ll cover it another time). But this is really good news to me, since this means my learning process itself is fine.&lt;/p&gt;
&lt;p&gt;So how about my work place? Here are just some of the problems I identified:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Our internal training mostly focuses on topics are a deep dive in certain areas, which can seem overwhelming for people who are new to them. It is also difficult to get on these training, due to the limit slots.&lt;/li&gt;
&lt;li&gt;There is a lack of structured learning in general within the company, what you learn and what you get to work on is mostly luck based. There isn&apos;t much support in terms of learning either, for graduates just came out of university it is easy to feel lost.&lt;/li&gt;
&lt;li&gt;Senior developers and coaches are too busy with client work and aren&apos;t too interested with helping junior people. Even if they wanted to, this can be quite difficult because they are based in different locations.&lt;/li&gt;
&lt;li&gt;There is little focus on training our non-technical people about the technical side of things. So very senior Business Analyst might not understand concept which a junior developer work with daily. Which makes the analyst&apos;s job difficult when they have to understand the challenges or gather valuable from stakeholders.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based on my own learning process, I came up with a new model of learning which can applied to teams. I call it Triad Learning.&lt;/p&gt;
&lt;h2&gt;Solution: Triad Learning&lt;/h2&gt;
&lt;p&gt;I decided to give it this name because it is made up of three parts: Presentation, Workshops, and 1:1 paring. Each part has its target audience and goal. Any of the individual parts on their own aren&apos;t groundbreaking, the fundamental idea here is that by combining them we create reinforced learning. Rather than just telling people about high level ideas, or do a deep dive on a topic people aren&apos;t familiar with. The goal of Triad training is to take some topic from the basic high level concept all the way down to practical hands on experiences.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/76VO4G38PKqeSq4S8sIggY/c5a1305065e1dac4a8a05bcac385b454/image.png&quot; alt=&quot;image&quot;&gt;&lt;/p&gt;
&lt;p&gt;The follow sections will be explained in a team environment, please refer to the five points I stated above if you need more information around applying this for your own individual learning.&lt;/p&gt;
&lt;h3&gt;Presentation&lt;/h3&gt;
&lt;p&gt;Talk or presentation is probably the most common verbal way to share knowledge. This type of training/learning aims to give large group of people a high level understanding about a topic.&lt;/p&gt;
&lt;p&gt;Example: if I want to train people around automation testing. I might want to give a presentation around why we test and how we might go about it. The target audience could be technical and non-technical people.&lt;/p&gt;
&lt;h3&gt;Workshops&lt;/h3&gt;
&lt;p&gt;Workshops are quite useful due to its discussion nature. These kind of sessions will offer two things: 1.) give people an opportunity to try something simple out with guidance and support. 2.) the other advantage is allowing people to listen to what others are talking about so more introverted people can also benefit from it.&lt;/p&gt;
&lt;p&gt;Example: using the automation testing as an example, this could be doing some TDD with a smaller group of people. The intended target audience is more developers in this case.&lt;/p&gt;
&lt;h3&gt;1:1 Paring&lt;/h3&gt;
&lt;p&gt;It is extremely frustrating when you can&apos;t get something to work. It is very helpful to have someone there who you can bounce ideas off. Even if they don&apos;t say much, explaining the problem to someone forces you to think about it more. Often, I end up solving my own problem half way through the explanation. The idea of 1:1 paring is for just that. To give other people the opportunities to approach the trainers and ask questions and bounce ideas off.&lt;/p&gt;
&lt;p&gt;Example: back to our testing example, this could someone asking how to mocking in Jest. Something that is very specific which doesn&apos;t interest everyone, but it will speed up the learning of the person if they had someone who could answer their questions or point them to the right direction.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;The main focus on Triad Learning is around reinforced learning. The first layer of the pyramid is to understand the high level concept. Then you go on to understand how it works through more hands on and a deeper dive. Lastly, you&apos;ll learn through more practical and encountering challenging problems.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Typescript: the key to a maintainable JavaScript codebase]]></title><description><![CDATA[<p>So here's a question, if someone told you to draw a sketch would you like to be rulers and grids OR do you prefer to just have a blank canvas? One gives you structure, guidance and assistance, while the other gives you freedom. This is exactly the same argument with statically typed language VS dynamically typed language. This question has been around for a long time, and no one has managed to settled the argument yet.</p>]]></description><link>https://blog.hao.dev/typescript-the-key-to-a-maintainable-javascript-codebase</link><guid isPermaLink="false">https://blog.hao.dev/typescript-the-key-to-a-maintainable-javascript-codebase</guid><pubDate>Wed, 15 Aug 2018 19:20:00 GMT</pubDate><content:encoded>&lt;p&gt;So here&apos;s a question, if someone told you to draw a sketch would you like to be rulers and grids OR do you prefer to just have a blank canvas? One gives you structure, guidance and assistance, while the other gives you freedom. This is exactly the same argument with statically typed language VS dynamically typed language. This question has been around for a long time, and no one has managed to settled the argument yet.&lt;/p&gt;&lt;p&gt;So what is the difference between static and dynamic typing, I hear you ask. Static type, like the name suggests, restricts a developer&apos;s ability to assign different data to data types by having rules in place. For example, static type means I won&apos;t be able to assign a number to a string typed variable. If I wanted I can still do the assignment, it is just I will have to convert that variable from string type to number type first. But why can&apos;t we just assign it, isn&apos;t the computer clever enough to work it out? That&apos;s exactly what a dynamically typed language does. It will allow flexible assignments between different types.&lt;/p&gt;
&lt;p&gt;Without going into much a deeper argument between static and dynamic typing, the easier high level comparison is that statically typed languages gives more confidence to developers when they work on a code base by catching errors early, whereas dynamically typed languages&apos; freedom means less interruptions during development and end up writing code faster.&lt;/p&gt;
&lt;h2&gt;So what&apos;s Typescript?&lt;/h2&gt;
&lt;p&gt;With the above long explanation of static and dynamic types, now let&apos;s talk about Typescript. Typescript was designed to allow people write typed JavaScript code. It is a open-source programming language developed and maintained by Microsoft. It was introduced back in Oct 2012, but it didn&apos;t pick up pace until recent years (see diagram below).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4NH6vMatuMGeC4GeYQi2a6/f894b04146086c486008a2fc96199a57/Screen_Shot_2018-08-14_at_4.56.39_pm.png&quot; alt=&quot;Screen Shot 2018-08-14 at 4.56.39 pm&quot;&gt;&lt;/p&gt;
&lt;p&gt;We briefly talked about the differences between static and dynamic typed languages, so Typescript is meant to be the perfect language that allows the best of both worlds. In reality, that is not quite 100% true but it is still extremely useful in my opinion.&lt;/p&gt;
&lt;h2&gt;Features&lt;/h2&gt;
&lt;p&gt;There are a lot of reasons to use Typescript in a project, I have listed a few which were most impactful for me. I recommend checking out the official &lt;a href=&quot;https://www.typescriptlang.org/index.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Typescript&lt;/a&gt; documentation and have a read yourself about all of its features.&lt;/p&gt;
&lt;h3&gt;Flexible &amp;#x26; optional to use&lt;/h3&gt;
&lt;p&gt;Some people might think Linters are more than enough to ensure the maintainability of their code, and this could be true. Just like any other tools, Typescript is not a one solution fits all language and it is not meant to be. What it does offer is choice, the ability to choose when to use static type in a dynamically typed language is very powerful. At the same time, it doesn&apos;t force you to use static typing everywhere like other static typed languages. This feature makes it very useful, because it means it can be added to any JavaScript project at any given time. Since all it takes is to renamed the file from &lt;code class=&quot;language-text&quot;&gt;.js&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;.ts&lt;/code&gt; then Typescript will take over. After the renaming I could literally leave the file as it is, and make no change at all and it would work since Typescript is an extension of JavaScript. So it will understand all the JavaScript syntax out of the box, then it is up to the developer to utilise the TypeScript features if they wish.&lt;/p&gt;
&lt;h3&gt;No more Babel&lt;/h3&gt;
&lt;p&gt;On the Frontend of web apps, People used to use Babel to compile latest JavaScript syntax to older JavaScript syntax so that the code can be understood by older browsers such as Internet Explorer (IE). The other good thing about Typescript is that you won&apos;t need to use Babel anymore, Typescript will also do this backwards compilation for you out of the box (configurable in the &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt;).&lt;/p&gt;
&lt;h3&gt;Enhanced IDE support&lt;/h3&gt;
&lt;p&gt;After using Typescript in a few projects, the thing I noticed was I didn&apos;t need to do inline documentation in the code nearly as much. With text-editors such as VS Code, when people get so much information when they cmd + hover on everything, there is really no need to document what parameters functions take or what kind of data each variable holds. Following code logic is also a lot easier, everything (e.g. function, class, variable) is just a Cmd + click away.&lt;/p&gt;
&lt;p&gt;The help doesn&apos;t just stop there, when writing code the most text-editors or IDEs will also use the information provided by Typescript and auto suggest things as developers code. Not only does this speed time development, it also means people make less mistakes with typos. Amazing stuff!&lt;/p&gt;
&lt;h3&gt;Good support in the wider community&lt;/h3&gt;
&lt;p&gt;Most common libraries today comes with or has a Typescript definition file (often in the form of &lt;code class=&quot;language-text&quot;&gt;index.d.ts&lt;/code&gt;). These definition files are generated in order to preserve the static typing used during development of those libraries. Still confused? Let&apos;s walk through an example. Assume that I&apos;m developing a Frontend app, in order to implement a new feature I want to use a library created by someone else so I don&apos;t reinvent the wheel. I install the library, then my text-editor automatically picks up the Typescript definition file that came with it. Now when I use the library I get all the useful intellisense information and features mentioned in the previous section on the new library. If there&apos;s something i&apos;m not sure about, for example I don&apos;t know how to call a function in this library. I don&apos;t even need to go to their official website to read the documentation, I could simply look at what Typescript tells me and get on with what I&apos;m trying to do.&lt;/p&gt;
&lt;h2&gt;When to use Typescript?&lt;/h2&gt;
&lt;p&gt;Javascript is a beautify language and its dynamic nature is amazing for prototyping. Try not to use Typescript in a small project where speed of delivery is key e.g. hackathons, small personal projects or product prototypes. Make a judgement call before start using it in all the projects. Don&apos;t give yourself unnecessary headaches or worse give your team unnecessary headaches for no reason.&lt;/p&gt;
&lt;p&gt;Typescript will require setup and config, and actively type everything to get maximum usage out of it. There are some configurations which might cause more pain points than others, so try out the configuration options and turn them off when they become a hassle to deal with. For example, I just recently worked on a project where I had to use Typescript with Redux, the experience wasn&apos;t great. I had to hack around the declaration of my Redux store to make Typescript happy. It would be wise at this point to either disable Typescript for those lines or tweak the Typescript config.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;Maintainability should always in the minds of responsible developers. At the end of the day, it is not just about writing code. Instead, as developers we should be able to defend our decisions and work when they are being questioned. In fact, we should also be proud to show off our work to other developers. Creating a maintainable clean codebase is a great place to start, and Typescript is a great tool for achieving that.&lt;/p&gt;
&lt;p&gt;Of course, Typescript is not the only solution to this problem. It might not even be the best tool, since I didn&apos;t have enough time to try out all of the alternative tools (such as Flow, Elm, and etc) and do a complete comparison. The best way to choose the best tool for your team or project is to spend a bit more time with each if possible, then make the call for yourself. If time is also a limiting factor, then Typescript is a solid tool which I can sensorily recommend.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Redux testing - Invariant violation error]]></title><description><![CDATA[<p>I came across this error "Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined" when trying to debug React/Redux app. This article walks through how I went about to solve it, and what was the issue.</p>]]></description><link>https://blog.hao.dev/redux-testing-invariant-violation-error</link><guid isPermaLink="false">https://blog.hao.dev/redux-testing-invariant-violation-error</guid><pubDate>Fri, 03 Aug 2018 18:21:00 GMT</pubDate><content:encoded>&lt;p&gt;I came across this error &quot;Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined&quot; when trying to debug React/Redux app. This article walks through how I went about to solve it, and what was the issue.&lt;/p&gt;&lt;p&gt;I came across this weird error when trying to test some React components.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Test suite failed to run

Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The structure is very simple, there is a Redux container wrapping a React presentational component with local state.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre style=&quot;counter-reset: linenumber 0&quot; class=&quot;language-javascript line-numbers&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Container component&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mapStateToProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; mapDispatchToProps &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
	mapStateToProps&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	mapDispatchToProps
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Container&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot; style=&quot;white-space: normal; width: auto; left: 0;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre style=&quot;counter-reset: linenumber 0&quot; class=&quot;language-javascript line-numbers&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Component&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Component&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Component&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;constructor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;props&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;props&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token literal-property property&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        
        &lt;span class=&quot;token operator&quot;&gt;...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        
    &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
            &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;Component&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
		&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
   	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot; style=&quot;white-space: normal; width: auto; left: 0;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I wrote tests for both of the files above, the container tests ran fine. However, when I try to run my test in Jest I&apos;d get this error.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Test suite failed to run

Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I checked my container&apos;s &lt;code class=&quot;language-text&quot;&gt;connect()&lt;/code&gt; function, but everything looks fine. After doing a little research, other people mentioned their issues were caused by one of the following issues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Circular dependencies between components&lt;/li&gt;
&lt;li&gt;Wrong usage of &lt;code class=&quot;language-text&quot;&gt;export&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;export default&lt;/code&gt; then imported the wrong way&lt;/li&gt;
&lt;li&gt;Used the connect function wrongly, passed the wrong parameters&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I checked against all these points, but none of it applies to the code I was working with.&lt;/p&gt;
&lt;p&gt;What&apos;s even more strange is that I could build and run the web app no problem. So this problem seems to only exist in Jest.&lt;/p&gt;
&lt;p&gt;So I decided to do more debugging around it, &quot;when stuck, trial and error&quot; right? I commented out the line which does the rendering, which looked something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre style=&quot;counter-reset: linenumber 0&quot; class=&quot;language-javascript line-numbers&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;should not fail&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// const wrapper = shallow(&amp;lt;Component /&gt;);&lt;/span&gt;

	&lt;span class=&quot;token function&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;toBe&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot; style=&quot;white-space: normal; width: auto; left: 0;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This test obviously failed, and it actually ran the test suite. Which is a good news, because I was never getting this far before. The fact the rendering is failing must mean my export was done right, or something related to it. So I went back and checked again, and same as before I found nothing.&lt;/p&gt;
&lt;p&gt;I tried commenting back all the shallow render and commented the import at the top of the file (see below). This also ran the test suite giving me the error that Component is undefined.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre style=&quot;counter-reset: linenumber 0&quot; class=&quot;language-javascript line-numbers&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// import Component from &apos;./Component&apos;;&lt;/span&gt;

&lt;span class=&quot;token function&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;should not fail&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; wrapper &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;shallow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;Component &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;span aria-hidden=&quot;true&quot; class=&quot;line-numbers-rows&quot; style=&quot;white-space: normal; width: auto; left: 0;&quot;&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;The issue&lt;/h2&gt;
&lt;p&gt;It turns out the problem was still circular dependency, but the shared dependency was something really stupid. Let me explain.&lt;/p&gt;
&lt;p&gt;I am using Typescript, I declared a type a few levels higher and then imported it in Component. This made that entire high level component dependent on the Component, thus causing the circular dependency.&lt;/p&gt;
&lt;p&gt;As suspected it was a simple fix, but I had to go through so many loops. For anyone getting this error, check the 3 points mentioned above because I think they will cover 99.99% of the cases.&lt;/p&gt;
&lt;p&gt;Also note to self:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Circular dependency doesn&apos;t get picked up by Typescript in React/Redux project. This could be a limitation of webpack or my webpack config, as when I used Typescript with rollup.js it did pick up circular dependency.&lt;/li&gt;
&lt;li&gt;Typescript will build React app with Circular dependency, but it won&apos;t run the test suite&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;The real life example&lt;/h2&gt;
&lt;p&gt;Here is the exact error I got so people can compare to see if they have the same problem. &lt;code class=&quot;language-text&quot;&gt;SpendPointsMenu&lt;/code&gt; in this case is the Component in my example above, and &lt;code class=&quot;language-text&quot;&gt;SpendPointsMenuContainer&lt;/code&gt; is obviously the container. &lt;code class=&quot;language-text&quot;&gt;Spend&lt;/code&gt; is the high level component where I was importing the type from. Don&apos;t worry too much about &lt;code class=&quot;language-text&quot;&gt;SpendPoints&lt;/code&gt; it just sits in between &lt;code class=&quot;language-text&quot;&gt;Spend&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;SpendPointsMenuContainer&lt;/code&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Test suite failed to run

Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined

      26 | )(SpendPointsMenu);
      27 |

      at invariant (node_modules/invariant/invariant.js:40:15)
      at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:101:29)
      at Object.&amp;lt;anonymous&gt; (src/views/spend/spendPoints/SpendPointsMenuContainer.tsx:28:141)
      at Object.&amp;lt;anonymous&gt; (src/views/spend/spendPoints/SpendPoints.tsx:13:33)
      at Object.&amp;lt;anonymous&gt; (src/views/spend/Spend.tsx:16:46)
      at Object.&amp;lt;anonymous&gt; (src/views/spend/spendPoints/SpendPointsMenu.tsx:20:40)
      at Object.&amp;lt;anonymous&gt; (src/views/spend/spendPoints/SpendPointsMenu.test.tsx:7:461)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;My first clue should have been: &lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;SpendPointsMenu.tsx is causing an error in SpendPointsMenu.test.tsx, that&apos;s fine because that&apos;s the file I trying to test. But why is Spend.tsx causing an error in SpendPointsMenu.tsx.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I over looked this clue, and ended up spending hours trying to fix something very simple.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why do projects always end up with tech debt?]]></title><description><![CDATA[<p>As a developer, I have both explained and heard others explain to less technical people about how the team need to slow down adding new features/functionalities and take care of some tech debt first. Often, the management asks why. Sometimes it is hard to communicate the idea across to some people. In fact, it is quite nature for tech debt to occur and it is no one's fault that this happens. It has always bothered me when tech debt become a point of tension between developers and management. Because I think it happens so often that everyone, developers and management, should understand why projects end up with tech debts, what they are and how to manage them.</p>]]></description><link>https://blog.hao.dev/why-do-projects-always-end-up-with-tech-debt</link><guid isPermaLink="false">https://blog.hao.dev/why-do-projects-always-end-up-with-tech-debt</guid><pubDate>Fri, 13 Jul 2018 17:26:00 GMT</pubDate><content:encoded>&lt;p&gt;As a developer, I have both explained and heard others explain to less technical people about how the team need to slow down adding new features/functionalities and take care of some tech debt first. Often, the management asks why. Sometimes it is hard to communicate the idea across to some people. In fact, it is quite nature for tech debt to occur and it is no one&apos;s fault that this happens. It has always bothered me when tech debt become a point of tension between developers and management. Because I think it happens so often that everyone, developers and management, should understand why projects end up with tech debts, what they are and how to manage them.&lt;/p&gt;&lt;p&gt;Tech debts is always talked about in development teams. It is a common questions in job interviews and it is often the hot topic of debate in team discussion. It is always hard to decide if time should be spend on building new features or reducing the amount of tech debts within a given project.&lt;/p&gt;
&lt;p&gt;When a team gets stuck in a loop of &quot;why should we reduce tech debts&quot; and &quot;why shouldn&apos;t we&quot;, it is extremely frustrating for everyone. This has always bothered me, because I don&apos;t think tech debt should become a point of tension in any project. It is completely natural to end up with tech debts in any project, as long as everyone has a good understanding of it, what matters the most is how to address it. I&apos;m not saying this kind of conversation isn&apos;t health, in fact, quite the opposite it is very health for the Product Owner (PO) to challenge the devs and vice versa. What isn&apos;t healthy to going around in circles, and debating about it week after week.&lt;/p&gt;
&lt;p&gt;This blog will explain what tech debts are from a developer&apos;s perspective since it is a technical problem at the end of the day. But I&apos;ll also explain why it might also be useful for devs to bear in mind of some the business concerns too instead of just pushing for reducing tech debts.&lt;/p&gt;
&lt;h2&gt;Why do we have tech debts?&lt;/h2&gt;
&lt;p&gt;Let&apos;s use library as an example, to illustrate why projects might end up tech debts.&lt;/p&gt;
&lt;p&gt;Imagine there&apos;s are a librarian, who works hard to keep 1,000 books tidy and organised in the right order on all of the book shelves. The above is what maintaining a project looks like. Sounds easy enough right?&lt;/p&gt;
&lt;p&gt;Now, let&apos;s say customers comes to the library everyday and sometimes books gets misplaced, books can also be borrowed and returned. On top of all that, each week 100 new books are also added to the library. That is a bit more work now right? As time goes on, it gets increasingly more challenging to achieve the original goal of keeping books tidy and organised. Simply because of how many books there are in the library. This is how it feels for a developer when they have to deliver value in an agile methodology such as scrum. There are a lot to balance out, e.g. time vs quality, off-the-shelf approach or use own implementation, performance, security and etc.&lt;/p&gt;
&lt;p&gt;As the number of books increase, sometimes books gets misplaced by customers but the librarian has too much to do and doesn&apos;t notice it. Books might be lost because something went wrong with borrowing or returning processes. Librarian might not have enough time to shelf all the books properly, and they just do their best and only put the books in the rough areas. All the these problems can occur in a project. As the project grows, same &quot;amount&quot; of work will take longer because there are more things to consider when delivering a new feature. It is like playing a game of Jenga, even with the same number of pieces, as the game goes on it comes harder to stay in the game. Not just because there are more variables to consider, but all the previous actions has a massive impact on the future actions, as well as limits what can and cannot be done.&lt;/p&gt;
&lt;p&gt;In some projects, it may have been worse. Assume, now the CEO of company decided they want to track the performance of each library by measure how many new books they can add on to the shelf each week. It seems crazy and illogic here right? But it does happen and maybe happens too often. This forces libraries to import more and more books to add on to the shelfs, and has a knock on effect on all of the problems mentioned above. This is why scrum recommends teams to maintain a constant velocity instead of strike for a higher number each sprint.&lt;/p&gt;
&lt;h2&gt;What are the tech debts and how to address them?&lt;/h2&gt;
&lt;p&gt;Tech debts comes in all sorts of shapes and forms. Here are just some of the common tech debts that I have seen first hand.&lt;/p&gt;
&lt;h3&gt;Duck tapped business logic&lt;/h3&gt;
&lt;p&gt;Now, we all work using some kind of agile methodology to ensure the product development can quickly pivot if and when the business requirement changes. This means requirements are not fed through until when they need to be pulled in. This means development teams are often unaware of business logics that&apos;s further than a few sprints away. This is where people having to build on top of any existing business logic. The lack of big picture at a given sprint and the fact these logic could have been the worked on by different people means that, the end result might look messy and is difficult to understand. Naturally, it will make any future work in this area more difficult, because people need to first understand what is going on before implementing anything new. In bad code bases, it can feel like walking around in a massive maze, where many routes will end up to be an dead end. It&apos;s a very time consuming and frustrating process.&lt;/p&gt;
&lt;p&gt;Not mentioning the fact, unless the existing code is fully understood, it is very easy to introduce new bugs. Unfortunately, the areas where this kind of duck tapping happens is also where the core business logic sits. So this could be financial calculations, complex user journey mapper and etc. Basically, things you don&apos;t want tripping over on production environment. This is why it is worth tackling them ASAP.&lt;/p&gt;
&lt;p&gt;When dealing with these issues, the core focus is to rewrite the code so that the logic is easier to understand. This kind of actions could massively speed up future development, because rather than looking at something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;face North
turn Left
turn Left
face South
turn Right&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;It will be as simple as:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;face West&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Pretty straight forward to see which is easier to understand.&lt;/p&gt;
&lt;h3&gt;Code cleaning&lt;/h3&gt;
&lt;p&gt;Code cleaning doesn&apos;t sounds like a necessity, but it can be hugely helpful. Unclean code makes it difficult to read and understand existing code base. Often a small amount of messy code and spiral out of control very quickly as the team loses grip on the &quot;right way&quot; of doing things.&lt;/p&gt;
&lt;p&gt;The common tasks of code cleaning includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Remove redundant code&lt;/li&gt;
&lt;li&gt;Remove badly formatted/organised code&lt;/li&gt;
&lt;li&gt;Remove inconsistent coding styles&lt;/li&gt;
&lt;li&gt;Adopting better coding practices&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All of the above should be fairly simple to understand. If you are still unclear, then think of it like tidying up a box of tens of thousands of Lego brick. The best way is to separate each type into its own compartment, then organise them in a logical way.&lt;/p&gt;
&lt;h3&gt;Performance improvements&lt;/h3&gt;
&lt;p&gt;When features are implementing quickly, most of the time it is probably NOT done in the most efficient way. Going back to previous point about the lack of big picture, which can definitely be challenging on one person is focusing on a very small part of a product or system. It might be that one aspect of the performance was missed during development. It could even be that, a while later someone has found a better way of doing the same thing.&lt;/p&gt;
&lt;p&gt;Depending on the situation, the performance improvements will be different. But a simple example might be that, instead of serving a default sized high quality images on a website, the team could export different sizes of the same image, so we can server different image sizes depending on the user&apos;s internet connection and device.&lt;/p&gt;
&lt;h3&gt;Other tech debts&lt;/h3&gt;
&lt;p&gt;Improving security: this is probably a bit rare for a product, since most likely everything has been thought of at the early stages of product live cycle. Since nowadays most products uses open source software and the open source nature means it is easy for hackers to find security flaws. Thus it is important for the team to review the libraries and frameworks used constantly, and  update to the latest version ASAP when bugs/security flaws has been revealed. &lt;/p&gt;
&lt;p&gt;Improving SEO performance: overtime changes made to the code base might have negatively impacted the website/app ranking on search engines.&lt;/p&gt;
&lt;p&gt;Improving accessibility: accessibility is often missed by development teams, but we should aim to build products that can be used easily by everyone.&lt;/p&gt;
&lt;p&gt;Better practices: as a team and product grows, it should be encouraged for the team to explore for better tools and practices to improve the ways of working, code quality, efficiency and etc.&lt;/p&gt;
&lt;h2&gt;Why might teams want to delay fixing tech debts?&lt;/h2&gt;
&lt;p&gt;For the business, tech debts is this vague and mysterious work that only the developers knows about. It is not hard to understand why management gets worried when the development teams says they need a month to work on tech debts without much justification. Unless people are being difficult and is being unreasonable, as long as they get the justification the management needs there is no reason why they would say no.&lt;/p&gt;
&lt;h3&gt;Understand priorities within the project&lt;/h3&gt;
&lt;p&gt;Tech debts is horrible, and teams should aim to reduce it as soon as possible. But it is worth to be mindful about the priorities in the current project. For example, any of the following would be good reasons to postpone tech debt for while, unless the tech debts is a massive blocker when it comes to delivering those goals. &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A big release is about to go out&lt;/li&gt;
&lt;li&gt;High priority feature addition/changes&lt;/li&gt;
&lt;li&gt;High priority bug fixes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Basically, use the best judgement to decided if it is the most appropriate time to be working on tech debts. For example, between releasing the next version of the product which contain important bug fixes and fixing some code formatting, it would be wise to focus on the release first then come back to the code format issues post release.&lt;/p&gt;
&lt;h2&gt;Summary&lt;/h2&gt;
&lt;p&gt;Hope this blog post has helped to clear up any questions about tech debts, and in some cases how to fix them as well. If you have any question feel free to get in touch!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migrated blog to Gatsby & Contentful]]></title><description><![CDATA[<p>One of the reason why there wasn't any blog post recently was because I was migrating my blog to a new platform...again. I can't remember how many times it has been in the past few years, and this is excluding the times where I investigated in other blogging platform but didn't decide to commit. But I'm happy to announce that this new platform should be much easier for me to create content going forward. In this post, I wanted to explain how it works now and why I've spend month migrating again.</p>]]></description><link>https://blog.hao.dev/migrated-blog-to-gatsby-and-contentful</link><guid isPermaLink="false">https://blog.hao.dev/migrated-blog-to-gatsby-and-contentful</guid><pubDate>Thu, 14 Jun 2018 22:19:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the reason why there wasn&apos;t any blog post recently was because I was migrating my blog to a new platform...again. I can&apos;t remember how many times it has been in the past few years, and this is excluding the times where I investigated in other blogging platform but didn&apos;t decide to commit. But I&apos;m happy to announce that this new platform should be much easier for me to create content going forward. In this post, I wanted to explain how it works now and why I&apos;ve spend month migrating again.&lt;/p&gt;&lt;h2&gt;Previous challenge&lt;/h2&gt;
&lt;p&gt;One of the reason why I haven&apos;t been pushing much content to my blog was because I kept having problems with my previous blogging solution - Hexo. For a long time, my local just wouldn&apos;t build due to some npm dependency issues. For months I couldn&apos;t publish anything because I simply can&apos;t get my local working to push them (If you are interested in how it worked you can find out more in a &lt;a href=&quot;http://haodong.io/migrating-blog-to-hexo&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;previous post&lt;/a&gt;). It is also a lot of hassel to publish anything even when there were no problems, for example I always had to do it on the same machine or I will need to install Node, Hexo and etc. It is more work than it sounds, especially on some platforms.&lt;/p&gt;
&lt;p&gt;When I finally got everything fixed, I was so fed up anyway and I wanted a better publishing experience. For a while I even considered ditching this site and going with Medium. Yep, I was that annoyed!&lt;/p&gt;
&lt;h2&gt;A new hope&lt;/h2&gt;
&lt;p&gt;So I have been using Netlify for a while to host my static blog, I was loving it. Then one day I was told there is something called Netlify CMS and I got really excited. All I&apos;m missing from my Hexo set up was literally a place to create and publish content. I also didn&apos;t want to make a code change, manage my own image insert every time. It is a pain to say the least.&lt;/p&gt;
&lt;p&gt;I tried out Netlify CMS, but it turns out too basic for what I need. I mean there isn&apos;t even a draft option, it is either publish blog or no blog. So unfortunate I had to pass on this solution. But this gave me more motivation to deep looking for other alternatives which worked better.&lt;/p&gt;
&lt;h2&gt;Errr... another new hope&lt;/h2&gt;
&lt;p&gt;Don&apos;t worry, this time it is for real. Someone at work was really impressed by it, so I was curious to find out what the fuss is all about. Then when I did look it up, I realised it is perfect for my blogging solution. If you haven&apos;t heard about it already, it is an amazing tool go check it out. But on a high level it is a CMS site, it provides APIs for anyone with access key to request data. The cherry on top is that it support graphQL, amazing right?&lt;/p&gt;
&lt;p&gt;So now the CMS site and connect is sorted out. The next step is trying to find a library to do the static site generation based on the data in Contenful. I have always wanted to use Gatsby in one of my projects, and this turns out to be a perfect opportunity. Gatsby uses React, so even less learning curve there, perfect!&lt;/p&gt;
&lt;p&gt;So in the past couple of months, I&apos;ve been finding time here and there to rebuild my blog from scratch using Gatsby. Then I re-implemented the more advanced features I had such as pagination, tag pages and etc. Now it is about 80% done, with some features still missing such as commenting.&lt;/p&gt;
&lt;h2&gt;The new work flow&lt;/h2&gt;
&lt;p&gt;With this new set up, which is amazing btw, everything becomes so simple compared to before.&lt;/p&gt;
&lt;p&gt;If I want to publish a new blog post, I would write it anywhere using markdown. Then copy and past into Contentful and publish it. I couldn&apos;t write on Contentful because when I tried I hit the max request limit due to its auto-save feature. So now I write it using Typora, but since it is markdown you can essencially write it anywhere.&lt;/p&gt;
&lt;p&gt;Once the blog is published, it will trigger a webhook on Netlify which will pull the Gatsby source code from my Github repo. Then the entire blogging site will get re-generated based on the lastest data from Contentful. No need to make code change, no need to open an IDE. I can do this on any machine that has a internet connection.&lt;/p&gt;
&lt;p&gt;If I ever want to add new features to the Gatsby app, then I just develop it as normal. When the source code is merged to master, it will also trigger a Netlify webhook which also triggars the build. About 30 seconds later I&apos;ll get the new feature on the live site.&lt;/p&gt;
&lt;h2&gt;Final Words&lt;/h2&gt;
&lt;p&gt;If you are looking to do the same, then apart from Contentful and Gatsby I&apos;d also recommend taking a look at gatsby-source-contentful library which saved me a lot of time.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Create a JavaScript library using Typescript, Jest and Rollup]]></title><description><![CDATA[<p>Recently I was working on a personal JS library <a href="https://github.com/hdong92/Serina" target="_blank" rel="nofollow noopener noreferrer">Serina</a>. It is a open source library, I wanted to use Typescript (TS) to ensure better stability due to it is static typed. It should make the code base more readable and therefore easier to contribution.</p>]]></description><link>https://blog.hao.dev/create-a-javascript-library-using-typescript-jest-and-rollup</link><guid isPermaLink="false">https://blog.hao.dev/create-a-javascript-library-using-typescript-jest-and-rollup</guid><pubDate>Mon, 27 Nov 2017 22:23:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently I was working on a personal JS library &lt;a href=&quot;https://github.com/hdong92/Serina&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Serina&lt;/a&gt;. It is a open source library, I wanted to use Typescript (TS) to ensure better stability due to it is static typed. It should make the code base more readable and therefore easier to contribution.&lt;/p&gt;&lt;p&gt;There are many ways to build projects, and Rollup is just one of them. I decided to go with Rollup because it is much lighter than something like Webpack, but it is still very well supported in terms of plugins. Most importantly, I&apos;ve used it in other projects and I&apos;m lazy :P All jokes aside, I have to say Rollup is my go to build tool right now.&lt;/p&gt;
&lt;p&gt;In this blog, I have broken down each part of the project into different sections, so it is easily to find alternative options if prefered. E.g. rather than using Typescript, you can simple skip it.&lt;/p&gt;
&lt;h2&gt;Setup core library&lt;/h2&gt;
&lt;p&gt;Like all JavaScript projects, first create project folder and within the project folder run &lt;code class=&quot;language-text&quot;&gt;npm init&lt;/code&gt; to initialise it.&lt;/p&gt;
&lt;p&gt;Create a &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; folder and then create an empty file inside it. It should now look something like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- package.json
    |-- src
        |-- myLib.ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inside the &lt;code class=&quot;language-text&quot;&gt;myLib.ts&lt;/code&gt; file is where the library logic will live, but let&apos;s do something simple for now. In the follow example, our library will simple take in a string then return it back. It is pretty useless, but for the purpose of this blog I&apos;m going to keep it like this so it is easier to follow.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;typescript&quot;&gt;&lt;pre class=&quot;language-typescript&quot;&gt;&lt;code class=&quot;language-typescript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; myLib &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;text&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; text&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Typescript&lt;/h2&gt;
&lt;p&gt;Install Typescript npm package by running &lt;code class=&quot;language-text&quot;&gt;npm i typescript&lt;/code&gt; (or use &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;It is always a good idea to configure the compiler options of Typescript to fit your coding pattern, and pick up on any potential issues with the code when it compiles. Create a file called &lt;code class=&quot;language-text&quot;&gt;tsconfig.json&lt;/code&gt; at root folder level with the following config:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;2.5.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;compilerOptions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;allowSyntheticDefaultImports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;declaration&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;diagnostics&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;target&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ES5&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;downlevelIteration&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;forceConsistentCasingInFileNames&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;noFallthroughCasesInSwitch&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;noImplicitReturns&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;noUnusedLocals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;noUnusedParameters&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;removeComments&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;noLib&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;moduleResolution&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;sourceMap&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;typeRoots&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;/.node_modules/@types&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;include&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;src/**/*&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;exclude&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;src/**/*.test.ts&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The folder should now look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- package.json
    |-- tsconfig.json
    |-- src
        |-- myLib.ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Code linting&lt;/h2&gt;
&lt;p&gt;It is always important to have linting setup for any project to ensure code standards and catch silly errors when coding. Since I&apos;m using Typescript, therefore &lt;code class=&quot;language-text&quot;&gt;TSlint&lt;/code&gt; is my choice here, but feel free to use &lt;code class=&quot;language-text&quot;&gt;ESlint&lt;/code&gt; if you wish to use pure JavaScript.&lt;/p&gt;
&lt;p&gt;Let&apos;s install tslint and the extra eslint rules by running &lt;code class=&quot;language-text&quot;&gt;npm i tslint tslint-eslint-rules&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now create &lt;code class=&quot;language-text&quot;&gt;tslint.json&lt;/code&gt; file in the root project directory.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- package.json
    |-- tsconfig.json
    |-- tslint.json
    |-- src
        |-- myLib.ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Inside the file, paste in something like the following. This is my prefered setup, but feel free to tweak it however you see fit.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;extends&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;tslint:recommended&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;tslint-eslint-rules&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;defaultSeverity&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;warning&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;jsRules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;rules&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;curly&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;ignore-same-line&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;interface-name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;never-prefix&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;max-line-length&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;180&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;object-literal-sort-keys&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;trailing-comma&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token property&quot;&gt;&quot;multiline&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;never&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token property&quot;&gt;&quot;singleline&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;never&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;arrow-parens&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;member-access&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;no-public&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;eofline&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;whitespace&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;check-module&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;no-angle-bracket-type-assertion&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;ordered-imports&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;quotemark&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;single&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;avoid-escape&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;object-curly-spacing&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;always&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;rulesDirectory&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For more information on the above rules, refer to the &lt;a href=&quot;https://palantir.github.io/tslint/rules/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Inside &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; I would recommend adding 2 npm scripts related to linting. &lt;code class=&quot;language-text&quot;&gt;npm run lint&lt;/code&gt; will check for all the TS files within &lt;code class=&quot;language-text&quot;&gt;src&lt;/code&gt; folder for mistakes, and &lt;code class=&quot;language-text&quot;&gt;npm run lint:fix&lt;/code&gt; will fix all the mistakes it can automatically (e.g. it is nice not having to go in different files and remove a whole bunch of white spaces).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;lint&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tslint -c tslint.json \&quot;src/**/*.ts\&quot;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&quot;lint:fix&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tslint --fix -c tslint.json \&quot;src/**/*.ts\&quot;&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Setup Test&lt;/h2&gt;
&lt;p&gt;In order to get test set up, we will need to install Jest and the related packages by running &lt;code class=&quot;language-text&quot;&gt;npm i jest ts-jest @types/jest&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Then add the following Jest config inside &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;. This will be used to pick up test files and etc.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  ...

  &lt;span class=&quot;token property&quot;&gt;&quot;jest&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;transformIgnorePatterns&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;node_modules/(?!shiva/)&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;globals&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;ts-jest&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;tsConfigFile&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;tsconfig.test.json&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;google&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;collectCoverageFrom&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;src/**/*.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;!src/**/*.schema.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;!src/schema/*.ts&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;testMatch&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/src/**/?(*.)test.ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/tests/**/?(*.)test.ts&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;coverageThreshold&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;global&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;branches&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;functions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;lines&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;statements&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;moduleFileExtensions&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;ts&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token string&quot;&gt;&quot;js&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;transform&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;\\.(ts)$&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&amp;lt;rootDir&gt;/node_modules/ts-jest/preprocessor.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;^.+\\.js$&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;babel-jest&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  ...
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Setup Build&lt;/h2&gt;
&lt;p&gt;To set up Rollup, first install Rollup and TS plugin by running &lt;code class=&quot;language-text&quot;&gt;npm i rollup rollup-plugin-typescript2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now create a &lt;code class=&quot;language-text&quot;&gt;rollup.config.js&lt;/code&gt; file in the root project directory. Inside this file create a simple rollup config like the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; typescript &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;rollup-plugin-typescript2&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;src/myLib.ts&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;MyLib&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;file&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;dist/myLib.js&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;umd&apos;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;plugins&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;typescript&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The project structure should now look like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- package.json
    |-- rollup.config.js
    |-- tsconfig.json
    |-- tslint.json
    |-- src
        |-- myLib.ts&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now, again inside &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; and add in some npm scripts for building the project. &lt;code class=&quot;language-text&quot;&gt;npm run build&lt;/code&gt; will call other npm scripts and then at the end compile the TS into JS and place it in the dist folder.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run lint &amp;amp;&amp;amp; npm run test &amp;amp;&amp;amp; npm run clean &amp;amp;&amp;amp; rollup --config&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token property&quot;&gt;&quot;clean&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rm -rf ./dist &amp;amp;&amp;amp; mkdir dist&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Icing on the cake&lt;/h2&gt;
&lt;p&gt;Since it is going to be an open source library, it is also worth considering doing the following to give people more confidence in the project:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a &lt;code class=&quot;language-text&quot;&gt;README.md&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;Add a &lt;code class=&quot;language-text&quot;&gt;LICENSE&lt;/code&gt; file so people know how they can use your code&lt;/li&gt;
&lt;li&gt;Set up CI/CD, and then place a badge in the README to show the code bade status (e.g. passing, failing)&lt;/li&gt;
&lt;li&gt;Consider adding an &lt;code class=&quot;language-text&quot;&gt;.editorconfig&lt;/code&gt; file&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;That&apos;s it!&lt;/h2&gt;
&lt;p&gt;Hope this was relatively straight forward to follow, I tried to cover all the aspects in this blog and answer the common questions. Please let me know in the comments if there&apos;s anything I missed or hit me up on Twitter!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Blogging with Hugo & Netlify & Forestry.io]]></title><description><![CDATA[<p>About 10 months ago I switched from Ghost blog to Hexo. I decided Hexo because it would be generating static site, which is great for SEO. It can be hosted on Github pages, which saves some £££. Then there is the most important reason: so I don't have to spend energy and time on maintaining the blog server and constantly migrating to newer Ghost versions.</p>]]></description><link>https://blog.hao.dev/blogging-with-hugo-and-netlify-and-forestry-io</link><guid isPermaLink="false">https://blog.hao.dev/blogging-with-hugo-and-netlify-and-forestry-io</guid><pubDate>Tue, 21 Nov 2017 18:23:00 GMT</pubDate><content:encoded>&lt;p&gt;About 10 months ago I switched from Ghost blog to Hexo. I decided Hexo because it would be generating static site, which is great for SEO. It can be hosted on Github pages, which saves some £££. Then there is the most important reason: so I don&apos;t have to spend energy and time on maintaining the blog server and constantly migrating to newer Ghost versions.&lt;/p&gt;&lt;p&gt;So why am I considering to change to another blog tool less than a year later? The most important reason is the complexity involved just to get something published. I found myself not wanting to write any blogs not because I don&apos;t have anything I want to write, but the publish process is so complex. Before when I was using Ghost, I could go on any computer type in an URL and start blogging. With Hexo, I need to install node, hexo and etc if I am not using my main computer. Then there&apos;s the manual work such as tagging blogs, adding in images and sorting out URL path names. If that is not bad enough, recently I have been getting all sorts of errors when I try to spin up Hexo locally, then after I updated the version it now no longer serves the local development site. So I decided enough is enough and researched around for a better way.&lt;/p&gt;
&lt;p&gt;My initial thought was maybe server Ghost blog locally then convert it into static site using &lt;code class=&quot;language-text&quot;&gt;buster&lt;/code&gt;. This would solve some of the problem, but I&apos;m basically back to square one minus the hosting and server mantainance problem. Then I read about a CMS tool called &lt;code class=&quot;language-text&quot;&gt;forestry.io&lt;/code&gt; which sounded very interesting. It will essentially act as the CMS of the blog site, which means no more &lt;code class=&quot;language-text&quot;&gt;hexo new --draft &quot;yet-another-blog-post&quot;&lt;/code&gt;! I could do it with a simple click on any computer browser like I used to be able to with Ghost (I&apos;ve also checked it works on mobile too). By no means is this a perfect solution, because the few pages which I had visitors will be lost after the site migration. And then there&apos;s the migration itself. But given this is the best solution I can find, here goes nothing...&lt;/p&gt;
&lt;h2&gt;Set up&lt;/h2&gt;
&lt;p&gt;Some prerequisites before we get started:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A computer to set everything up initially e.g. Hugo, source code repo&lt;/li&gt;
&lt;li&gt;A &lt;code class=&quot;language-text&quot;&gt;Github&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;Bitbucket&lt;/code&gt;/&lt;code class=&quot;language-text&quot;&gt;Gitlab&lt;/code&gt; account to keep the source code of the site&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;Netlify&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;Github pages&lt;/code&gt; or any alternatives supported by &lt;code class=&quot;language-text&quot;&gt;forestry.io&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;And finally &lt;code class=&quot;language-text&quot;&gt;forestry.io&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are different ways of setting this up, by no means this is the best way. But I think this is the easiest way to follow.&lt;/p&gt;
&lt;h3&gt;Create Repository&lt;/h3&gt;
&lt;p&gt;Create a repo on any source code hosting site of your choice. I&apos;m using Bitbucket because it offers private repo hosting for free.&lt;/p&gt;
&lt;p&gt;Then &lt;code class=&quot;language-text&quot;&gt;git clone&lt;/code&gt; the repo into a directory on your computer.&lt;/p&gt;
&lt;h3&gt;Installing Hugo&lt;/h3&gt;
&lt;p&gt;The installation is pretty straight forward. Since I&apos;m using a Mac, so the installation commands will be for Mac OS. If you are using Windows or Linux, please refer to the &lt;a href=&quot;https://gohugo.io/getting-started/installing&quot; title=&quot;Hugo Official Site&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;offical guide&lt;/a&gt;.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;brew install hugo

cd ~/yourFolder

hugo new site blog&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This will create a &lt;code class=&quot;language-text&quot;&gt;blog&lt;/code&gt; folder within the repo folder. It should look something like this right now.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- blog
        |-- config.toml
        |-- archetypes
        |-- content
        |-- data
        |-- layouts
        |-- static&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The next thing to do is to move everything out of the &lt;code class=&quot;language-text&quot;&gt;blog&lt;/code&gt; to the level above. It should look something like this after you are done:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;yourFolder
    |-- config.toml
    |-- archetypes
    |-- content
    |-- data
    |-- layouts
    |-- static&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then follow instructions on installing theme, and update the config. There should be plenty of documentation in the Hugo &lt;a href=&quot;https://gohugo.io/getting-started/installing&quot; title=&quot;Hugo Official Site&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;offical guide&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once everything is done push it to the &lt;code class=&quot;language-text&quot;&gt;master&lt;/code&gt; branch.&lt;/p&gt;
&lt;h3&gt;Setting up hosting&lt;/h3&gt;
&lt;p&gt;I am personally using &lt;a href=&quot;https://app.netlify.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Netlify&lt;/a&gt;, because it is awesome and easy to set up. But you will get everything you need using Github pages too.&lt;/p&gt;
&lt;p&gt;This step is as easy as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Login into Netlify using your Github/Bitbucket/Gitlab OAuth.&lt;/li&gt;
&lt;li&gt;Select the repository&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then fill in these values in the relevant fields&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Build command: hugo
Publish directory: public&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have added a &lt;code class=&quot;language-text&quot;&gt;netlify.toml&lt;/code&gt; file to the repo (next to &lt;code class=&quot;language-text&quot;&gt;config.toml&lt;/code&gt;) to state the production hugo version as well. Not sure how relevant this will be, but I just wanted to be safe.&lt;/p&gt;
&lt;p&gt;For more information, Hugo has a &lt;a href=&quot;https://gohugo.io/hosting-and-deployment/hosting-on-netlify&quot; title=&quot;Hosting Hugo on Netlify&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;detailed documentation&lt;/a&gt; for this step as well.&lt;/p&gt;
&lt;h3&gt;Setting up CMS&lt;/h3&gt;
&lt;p&gt;This step is even simpler! Go to &lt;a href=&quot;https://app.forestry.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;forestry.io&lt;/a&gt; and then login with your relevant Github/Bitbucket/Gitlab OAuth. Go through the simple wizard, then you&apos;ll be taken to the CMS page. It is so simple, there&apos;s no hassle at all!&lt;/p&gt;
&lt;h3&gt;All done!&lt;/h3&gt;
&lt;p&gt;That&apos;s it, now you can blog where ever and when ever! Like I mentioned already, &lt;code class=&quot;language-text&quot;&gt;forestry.io&lt;/code&gt; even support mobile devices, so you can do that too!&lt;/p&gt;
&lt;h3&gt;Migration&lt;/h3&gt;
&lt;p&gt;The last step is off course migrating from which ever platform you are using right now.&lt;/p&gt;
&lt;h2&gt;Final thoughts&lt;/h2&gt;
&lt;p&gt;This was very cool, but I personally had some issues with theme styling and decided to stick with Hexo. But for anyone who is setting up a new blog, since this is so easy it is almost silly not to give this a go first!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[JavaScript Events]]></title><description><![CDATA[<p>We are always using JavaScript events when developing web application, it is what makes it interactive at the end of the day. In this blog, I'll go over what it is and the different ways to intercepted a JavaScript event.</p>]]></description><link>https://blog.hao.dev/javascript-events</link><guid isPermaLink="false">https://blog.hao.dev/javascript-events</guid><pubDate>Mon, 02 Oct 2017 19:00:00 GMT</pubDate><content:encoded>&lt;p&gt;We are always using JavaScript events when developing web application, it is what makes it interactive at the end of the day. In this blog, I&apos;ll go over what it is and the different ways to intercepted a JavaScript event.&lt;/p&gt;&lt;h2&gt;JS events in HTML&lt;/h2&gt;
&lt;p&gt;HTML events are “things&quot; that happen to HTML elements. We can use JavaScript to “react” to these events. Events can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An HTML web page has finished loading&lt;/li&gt;
&lt;li&gt;An HTML input field was changed&lt;/li&gt;
&lt;li&gt;An HTML button was clicked&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Here are some common HTML events:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onchange&lt;/code&gt; - An HTML element has been changed&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onclick&lt;/code&gt; - The user clicks an HTML element&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onmouseover&lt;/code&gt; - The user moves the mouse over an HTML element&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onmouseout&lt;/code&gt; - The user moves the mouse away from an HTML element&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;onkeydown&lt;/code&gt; - The user pushes a keyboard key&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;unload&lt;/code&gt; - The browser has finished loading the page&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;onclick&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value javascript language-javascript&quot;&gt;&lt;span class=&quot;token function&quot;&gt;displayDate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;The time is?&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;In summary, this section explained how to use events in HTML elements.&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://www.w3schools.com/js/js_events.asp&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://www.w3schools.com/js/js_events.asp&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;JS events in JS&lt;/h2&gt;
&lt;p&gt;Events is the fundamentals to how modern framework achieves data bindings. Angular 1.x is probably the best example, its two way data binding on the lowest level is events.&lt;/p&gt;
&lt;h3&gt;How to use it&lt;/h3&gt;
&lt;p&gt; event = new Event(typeArg, eventInit);&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;typeArg&lt;/code&gt; is a DOMString representing the name of the event.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;evenInit&lt;/code&gt; is optional object type, that can take in the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;bubbles&lt;/code&gt; - A boolean indicating whether the event bubbles. Default value is &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;cancelable&lt;/code&gt; - A boolean indicating whether the event can be canceled. Default value is &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;composed&lt;/code&gt; - A boolean indicating whether the event will trigger listeners outside of a shadow root. Default value is &lt;code class=&quot;language-text&quot;&gt;false&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// create a look event that bubbles up and cannot be canceled&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; evt &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Event&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;look&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;bubbles&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token string-property property&quot;&gt;&quot;cancelable&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;dispatchEvent&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;evt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Intercepting an event&lt;/h3&gt;
&lt;p&gt;There are times when you want to stop an event from doing what it. My most recent experience was, closing an menu when the user clicks anywhere else on the screen. So the code logic was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Click anywhere on the screen and hide menu&lt;/li&gt;
&lt;li&gt;When click on the menu, override the above logic and do not hide menu&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So now the question is how do we override an event? There are a number of ways to achieve this, we will try to cover most of them here.&lt;/p&gt;
&lt;h4&gt;1. Event.preventDefault()&lt;/h4&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;preventDefault()&lt;/code&gt; method tells the user agent that if the event does not get explicitly handled, its default action should not be taken as it normally would be. The event continues to propagate as usual, unless one of its event listeners calls &lt;code class=&quot;language-text&quot;&gt;stopPropagation()&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;stopImmediatePropagation()&lt;/code&gt;, either of which terminates propagation at once.&lt;/p&gt;
&lt;p&gt;Usage: Prevent a user from checking a checkbox.&lt;/p&gt;
&lt;p&gt;Tip: If an event is not &lt;code class=&quot;language-text&quot;&gt;cancelable&lt;/code&gt;, calling &lt;code class=&quot;language-text&quot;&gt;preventDefault()&lt;/code&gt; will throw errors. It is recommended to check on  &lt;code class=&quot;language-text&quot;&gt;cancelable&lt;/code&gt; before invoking &lt;code class=&quot;language-text&quot;&gt;preventDefault()&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;2. Event.stopPropagation()&lt;/h4&gt;
&lt;p&gt;Prevents further propagation of the current event in the capturing and bubbling phases.&lt;/p&gt;
&lt;h4&gt;3. Event.stopImmediatePropagation()&lt;/h4&gt;
&lt;p&gt;Prevents other listeners of the same event from being called. It is the same as &lt;code class=&quot;language-text&quot;&gt;event.stopPropagation()&lt;/code&gt; plus preventing other registered event handlers on the same element to be executed. So it does not prevent the default action for an event, such as following a clicked link.&lt;/p&gt;
&lt;h4&gt;4. Return false&lt;/h4&gt;
&lt;p&gt;Return false as an event is basically the same as calling both, &lt;code class=&quot;language-text&quot;&gt;event.stopPropagation()&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;event.preventDefault()&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Tip: return false does not prevent the event from bubbling up in “normal&quot; (non-jQuery) event handlers but still prevents the default action.&lt;/p&gt;
&lt;p&gt;Source: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/Event/Event&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://developer.mozilla.org/en-US/docs/Web/API/Event/Event&lt;/a&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Never Add People to a Project Just Before the Deadline]]></title><description><![CDATA[<p>For all the project manager, delivery manager, program manager and anyone else who manages a project’s delivery out there, seriously, stop assigning people on projects right before the deadline thinking that it will help you to deliver on time. Because, surprise, it doesn’t!</p>]]></description><link>https://blog.hao.dev/never-add-people-to-a-project-just-before-the-deadline</link><guid isPermaLink="false">https://blog.hao.dev/never-add-people-to-a-project-just-before-the-deadline</guid><pubDate>Sat, 22 Jul 2017 15:24:00 GMT</pubDate><content:encoded>&lt;p&gt;For all the project manager, delivery manager, program manager and anyone else who manages a project’s delivery out there, seriously, stop assigning people on projects right before the deadline thinking that it will help you to deliver on time. Because, surprise, it doesn’t!&lt;/p&gt;&lt;p&gt;I’m not going to give you any lecture based on business book (which anyone who manages a project should already know about), how about some real life experiences? I have been on both sides of this kind of situations, as someone who are thrown into an unknown project and has someone joining the existing team. So believe me when I say putting more people on a project last minute doesn’t work.&lt;/p&gt;
&lt;h2&gt;The problems&lt;/h2&gt;
&lt;h3&gt;Last minute joiners lacks the commitment and motivation&lt;/h3&gt;
&lt;p&gt;Let’s start with something as simple as this, imagine you just came off a project. You are thinking, what a great time to do some self study. But guess what, you have been told to join another project tomorrow (or in some cases on the same day). So not only does it throws all your plan out of the window, but also get ready for some intense delivery roller coasters.&lt;/p&gt;
&lt;p&gt;The next day, you come to office not knowing where you need to sit or what you should be doing. If you are lucky you get a good introduction to the project background, the state of the project, what needs to be done before the deadline. If you are not lucky, then there won’t be much introduction at all. Either way you will have a lot of questions. When you try to understand more about the project to do your job properly, you will end up pulling people away from their work and they will most likely be stressed out. Which is not a fun experience for either of you.&lt;/p&gt;
&lt;p&gt;As a new joiner it is normal to think that they are only here to help, if the project doesn’t deliver on time they are not the ones to blame. Even if the project delivers on time, they won’t get much credit from it either. At the end of the first day, they will be sitting there feeling confused, stressed out, miserable and want out from the project. How can they be engage with project, and be motivated to delivery on time if there is no commitment?&lt;/p&gt;
&lt;h3&gt;People asking why not this, or this, or this…&lt;/h3&gt;
&lt;p&gt;The biggest benefit of having a consistent team is that everyone understands why things are done the way it was. People are also on the same page in terms of ways of working.&lt;/p&gt;
&lt;p&gt;Bringing in new people will change this. Normally, asking questions and challenging the current ways of working is health since it ensures continuous improvements over time. However, when there’s 2 weeks left in the project and you have people challenging the fundamentals of what was done already, then it can be a problem.&lt;/p&gt;
&lt;p&gt;Sure, someone could explain to the new project joiners and say now is not the time and we will log those suggestions and tackle them post release. But it won’t be the last time this type of conversations come up until the project finishes. This type of conversations are very expensive since it is wasting at least 2 people’s time (from the delivery point of view).&lt;/p&gt;
&lt;h3&gt;Lack of project understand/knowledge&lt;/h3&gt;
&lt;p&gt;How can someone do a good job, they don’t have all the information they need. It is like asking a school kid to do University assignments. They are simple not ready to tackle those problems, as a result they will need a lot of support and help from existing team members. It will be hard to say no in this kind of situations, because the task is too challenging for people who are new to the project.&lt;/p&gt;
&lt;p&gt;In this type of situations, the team could leave the new joiners to fend for themselves, or have an existing team member to help them and tackle the problem together.&lt;/p&gt;
&lt;p&gt;If the team decides not to allocate another person to help the new joiner, then they will take a long time to finish the task. Even after they finish, the review stage will take up a lot of time to ensure work quality, accuracy, and consistency.&lt;/p&gt;
&lt;p&gt;If an existing team member is allocated to help out, then the reality is that normally the task will take longer to complete because answering questions, explaining and etc will eat up time. So a task that might take a day for the existing team member to finish individually, now it will take up 2 days (4 man days because there are 2 people working on it).&lt;/p&gt;
&lt;h3&gt;Lack the technical skills set&lt;/h3&gt;
&lt;p&gt;This is the worse kind of people put on a project. If there is a project that requires a specific skill set, putting someone who doesn’t have that skill does nothing to the project but slow the existing team down.&lt;/p&gt;
&lt;h2&gt;What should teams do&lt;/h2&gt;
&lt;h3&gt;Extend the deadline&lt;/h3&gt;
&lt;p&gt;I think if a project is being delayed, extending the deadline is the best option. This will guarantee the best quality and probably use the least amount of man days compared to bring in new joiners. Knowing there are enough time, will make people less stressed out and might even make them be more efficient.&lt;/p&gt;
&lt;h3&gt;Bring in people early&lt;/h3&gt;
&lt;p&gt;There is nothing wrong with bringing in people to help the project, the key thing is not to do it last minute. Bring people in before reaching half of the project duration, and give the team to adapt this change. Adding new joiners is like throwing a rock into a stable lake surface. It will take time for the ripple to stop and settle back to the original state.&lt;/p&gt;
&lt;h3&gt;Have a big team at the start, then take people off&lt;/h3&gt;
&lt;p&gt;This seems stupid but, it is always easier to take people off a project than to add someone in later on. So if there is a risk, then just put in some additional people at the start. If later the team feels there’s no point to keep everyone on, then take some people off it.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;The above problems are very general and will apply to most projects, but it is especially the case for software development. These problems are not new concepts, but for some reason projects always end up in a situation where more people need to be added to a project a few weeks before the deadline to meet the deadline. There is no excuses, it is just bad management and shows the person in charge doesn’t know what they are doing.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Use ZSH Shell as Integrated Terminal in Visual Studio Code]]></title><description><![CDATA[<p>I have migrated to ZSH shell long ago, and never looked back. It was weird when I opened up and saw the normal shell in VS Code.</p>]]></description><link>https://blog.hao.dev/use-zsh-shell-as-integrated-terminal-in-visual-studio-code</link><guid isPermaLink="false">https://blog.hao.dev/use-zsh-shell-as-integrated-terminal-in-visual-studio-code</guid><pubDate>Fri, 23 Jun 2017 18:18:00 GMT</pubDate><content:encoded>&lt;p&gt;I have migrated to ZSH shell long ago, and never looked back. It was weird when I opened up and saw the normal shell in VS Code.&lt;/p&gt;&lt;hr&gt;
&lt;p&gt;Update: 10th April 2022&lt;/p&gt;
&lt;p&gt;As mentioned by Jeffrey Thomas in the comments, this post is now out of date. The correct configuration is now:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;  &lt;span class=&quot;token property&quot;&gt;&quot;terminal.integrated.defaultProfile.osx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;zsh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For anyone who&apos;s trying to get this working on Mac with M1 chip, please check out &lt;a href=&quot;/setting-up-zsh-with-vs-code-on-apple-silicon-mac-m1-chip&quot;&gt;Setting up ZSH with VS Code on Apple silicon Mac (M1 chip)&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;It took me a while to figure it out, but it was as simple as this one line change in the settings.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;terminal.integrated.shell.osx&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/bin/zsh&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Instructions&lt;/h2&gt;
&lt;h3&gt;1. Find the setting&lt;/h3&gt;
&lt;p&gt;Open up VS Code Settings, then search for &lt;code class=&quot;language-text&quot;&gt;terminal.integrated.shell.osx&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;terminal.integrated.shell.linux&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;terminal.integrated.shell.windows&lt;/code&gt; depending on your platform.&lt;/p&gt;
&lt;h3&gt;2. Copy it over to User Settings&lt;/h3&gt;
&lt;p&gt;Note: Make sure you have User Settings selected. Workspace Settings only apply to the current folder or project. Whereas User Settings will change the default behaviour everywhere.&lt;/p&gt;
&lt;p&gt;After searching the above string, you should see the filtered list showing the relevant settings. If you hover over the line then there’s a pencil (edit) button. Click on the button and select copy to settings.&lt;/p&gt;
&lt;h3&gt;3. Update the setting&lt;/h3&gt;
&lt;p&gt;After copying it over, change &lt;code class=&quot;language-text&quot;&gt;/bin/sh&lt;/code&gt; to &lt;code class=&quot;language-text&quot;&gt;/bin/zsh&lt;/code&gt; or whatever shell you prefer to use.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[The simplest answer to - should I use Redux in my React app?]]></title><description><![CDATA[<p>There are a lot of good answers on the internet for this question already, I have no doubt that they all provide a much more complete explanation. But sometimes a high level understanding is enough to make an decision, instead of wasting hours of research then come to the decision that it is not needed. In this article, I’ll give the shortest answer I can come up with to answer this question.</p>]]></description><link>https://blog.hao.dev/the-simplest-answer-to-should-i-use-redux-in-my-react-app</link><guid isPermaLink="false">https://blog.hao.dev/the-simplest-answer-to-should-i-use-redux-in-my-react-app</guid><pubDate>Sun, 11 Jun 2017 21:12:00 GMT</pubDate><content:encoded>&lt;p&gt;There are a lot of good answers on the internet for this question already, I have no doubt that they all provide a much more complete explanation. But sometimes a high level understanding is enough to make an decision, instead of wasting hours of research then come to the decision that it is not needed. In this article, I’ll give the shortest answer I can come up with to answer this question.&lt;/p&gt;&lt;p&gt;And here it is:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6GR9hh0WSAImg6IUAgMyw6/be17e8e463b00794414ccec09402f44a/react-vs-redux.svg&quot; alt=&quot;react-vs-redux&quot;&gt;&lt;/p&gt;
&lt;p&gt;The diagram is too vague? Need more explanation? Ok, assuming you have an application that is made up with components Root, A, B, C, D, E and F. Root component is the top level, and then state is passed to A and D respectively, these components then have child components of B and E respectively and etc. If the components of share states then using Redux in your application would make development a lot easier.&lt;/p&gt;
&lt;p&gt;Compare the above examples, which would be easier to sync state between B and E? In the React example, state will flow like this B =&gt; A =&gt; Root =&gt; D =&gt; E. Whereas in the Redux example, state will flow from B =&gt; store (not shown in the diagram) =&gt; E.&lt;/p&gt;
&lt;p&gt;I hope this article was useful to some of you. For a more detailed explanation of what Redux is and how it works I recommend the &lt;a href=&quot;http://redux.js.org/docs/introduction/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt; for Redux. If you want to understand more about React states, here is the &lt;a href=&quot;https://facebook.github.io/react/docs/thinking-in-react.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official documentation&lt;/a&gt; as well.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[What it takes to learning a new skill]]></title><description><![CDATA[<p>Recently, I have been helping out with designing the new competency model for the my current employer. It has been a great learning experience, and as a result I have been trying to apply some of the learning to my personal development. This blog describes how I am planning to structure my own learning in the future.</p>]]></description><link>https://blog.hao.dev/what-it-takes-to-learning-a-new-skill</link><guid isPermaLink="false">https://blog.hao.dev/what-it-takes-to-learning-a-new-skill</guid><pubDate>Sun, 07 May 2017 19:05:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I have been helping out with designing the new competency model for the my current employer. It has been a great learning experience, and as a result I have been trying to apply some of the learning to my personal development. This blog describes how I am planning to structure my own learning in the future.&lt;/p&gt;&lt;h2&gt;The different stages of learning&lt;/h2&gt;
&lt;p&gt;When we describe how good someone is at something, we often compare them to ourselves and rated them based on how good we are. If we are just a beginner then someone who is proficient will be rated as &quot;amazing&quot; when in fact they still have a lot of room to improve. This kind of inconsistency will cause a lot of problem in companies where rating and review needs to be
consistent to ensure fairness.&lt;/p&gt;
&lt;p&gt;So how should we rate people&apos;s competency level?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;No knowledge&lt;/strong&gt; - never encounter this before&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Awareness&lt;/strong&gt; - can talk a little bit about the given skill e.g. framework, library or tool&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Learn&lt;/strong&gt; - currently developing this skill, can complete some basic tasks but still requires supervision and support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Use&lt;/strong&gt; - able to start from scratch and complete a complex task without any support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Teach&lt;/strong&gt; - not only do they have the skill, they understand the best practices and can coach others in this area.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expert&lt;/strong&gt; - leading the exploration, producing work that influences the development and progress of this field&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;An example&lt;/h3&gt;
&lt;p&gt;The descriptions above might be somewhat abstract and is too high level. Let me take you through an example I always use when explaining how to learn CSS.&lt;/p&gt;
&lt;h4&gt;Stage 1 - no knowledge&lt;/h4&gt;
&lt;p&gt;Someone who is not technical at all, and has no idea how CSS works on a website.&lt;/p&gt;
&lt;h4&gt;Stage 2 - awareness&lt;/h4&gt;
&lt;p&gt;Someone started creating simple static web pages, and they want to be able to changing colours and layouts on the page. After some research they understand they need to use CSS to do so.&lt;/p&gt;
&lt;h4&gt;Stage 3 - learn&lt;/h4&gt;
&lt;p&gt;Someone who has started using simple CSS to change the look and feel of a web page, but still lack the ability to implement complex designs. This person also doesn&apos;t fully understand the reason behind how everything works.&lt;/p&gt;
&lt;h4&gt;Stage 4 - use&lt;/h4&gt;
&lt;p&gt;A web developer with a couple years of experience, will be able to implement a web site based on designed wireframes.&lt;/p&gt;
&lt;h4&gt;Stage 5 - teach&lt;/h4&gt;
&lt;p&gt;A more experienced web developer who has read in the field about CSS methodologies and best practices in organising code to ensure consistency and maintainability.&lt;/p&gt;
&lt;h4&gt;Stage 6 - expert&lt;/h4&gt;
&lt;p&gt;For this stage, I think of someone who has created a CSS framework that is well received by the community.&lt;/p&gt;
&lt;h2&gt;My biggest take aways&lt;/h2&gt;
&lt;h3&gt;Create a learning catalogue&lt;/h3&gt;
&lt;p&gt;When learning something new it is easy to get lost in all the different opinions and conversations around the field. It is important to do some research and list out all the important topics to cover before diving in head first. What I always find myself doing before is getting lost in the minor topics such as &quot;how to optimise x&quot;, &quot;comparison between y and x&quot; and etc. These type of knowledge can be very useful when I get to the &quot;use&quot; stage, because knowing about all these would help me to understand the best practices and thus teach others about it. But being exposed to it too early will distract me away from the more important things.&lt;/p&gt;
&lt;p&gt;Say I&apos;m learning about a new JavaScript framework, by creating a learning catalogue, I can be confident that if I follow it through I will complete the &quot;learn&quot; stage and get to the &quot;use&quot; stage. However, if I&apos;m still at the &quot;learn&quot; stage and I&apos;m already looking at minor topics I mentioned above then it doesn&apos;t help me to move on to the &quot;use&quot; stage. The most obvious problem is that, I find myself in situations where I know some of the stuff other do not. But at the same time I wish I had a better understanding of the more fundamentals.&lt;/p&gt;
&lt;h3&gt;Personal projects helps to learn&lt;/h3&gt;
&lt;p&gt;Some might argue that different people learn differently, but here I&apos;m going to recommend no matter what your learning process is, be sure to create something as you learn.&lt;/p&gt;
&lt;p&gt;I have 3 reasons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Getting your hands dirty will help your brain to strengthen the neural pathways created by reading/watching. So you remembered things better and will be able to recall things later.&lt;/li&gt;
&lt;li&gt;Nowadays, it is important to show evidence of your work when applying for jobs. The ability to demonstrate you skills is essential for getting a senior role. For example, if I&apos;m applying for a tech lead role. I&apos;m going to need more than a few line of text on a CV to convince them that I have the right skills for the job.&lt;/li&gt;
&lt;li&gt;The last reason is actually the most important one for me. When trying to do something, no doubt I&apos;ll encounter problems. This forces me to think about digest the spoon fed information better.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Learn intelligently&lt;/h3&gt;
&lt;p&gt;In the world of software engineering, there are a lot of old technology and even more new technology. Learning new things is always more exciting and probably will provide better job security. But it is not always the case, for example languages such as C is 45 years old, it was introduced back in 1972. But it is still widely used today.&lt;/p&gt;
&lt;p&gt;When people ask me where they should start when learning JavaScript framework. Instead of telling them go and learn React .jsor Vue.js (which in my opinion are the 2 best frameworks to learn right now), I would recommend them to go back and learn Angular.js 1. Why? Because it is easy!&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The syntax is user friendly&lt;/li&gt;
&lt;li&gt;When working on simple single page applications the set up is straight forward&lt;/li&gt;
&lt;li&gt;There are a lot of resources online, in terms of plugins, questions and answers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;jQuery is my first Javascript framework, but creating a single page application with it is messy and confusing for new beginners. Creating an application with Angular 1 people will learn the basics of web development. Even though the philosophy is not as up-to-date as the component based development, it creates a much better learning process in my opinion. This reminds me in school textbooks how sometimes children are taught the wrong information in Science, because the correct explanation is too advanced for them to understand (if you are interested I&apos;m referring to the &lt;a href=&quot;https://www.edplace.com/userfiles/image/Atomic%20Structure%201.jpg&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;atomic structure diagrams&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;After knowing the fundamentals, learning the other JavaScript frameworks will be easier because the person would understand why things happen certain ways. However, it is not easy to understand what is the best way to learn. Having a good coach to support you is extremely useful.&lt;/p&gt;
&lt;h3&gt;Find a role that creates opportunity to learn&lt;/h3&gt;
&lt;p&gt;When working as the same role for too long, it could prevent a person to reaching the next step. Not because the person doesn&apos;t want to learn, instead the role simply does not provide the opportunities to do so. For example, code maintenance can teach me a lot about how other people work and this would be extremely useful I was at the &quot;learn&quot; stage. However, I could never reach &quot;expert&quot; stage by maintaining code everyday. This might not be a problem if being an expert is not one of your career goals, but if it was then I would recommend changing the role or job.&lt;/p&gt;
&lt;p&gt;I often find a person can learn 80% of the skills related to his/her work within 3 months. Continuing doing the same thing will have little benefit after that. Of course this is just an timeframe I came up with based on my experience, so take it with a pinch of salt.&lt;/p&gt;
&lt;h2&gt;Final words&lt;/h2&gt;
&lt;p&gt;This blog was more or less a brain dump of the ideas I have been having recently after helping to develop the new competency model. I hope it was a useful read, and please leave comments to let me know how you found it.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[My Recent Assertiveness Training]]></title><description><![CDATA[<p>I am always trying to be polite, friendly and calm at work. It is called being professional, and it is especially important for people working in professional services. The word professional is in the name kind of gave it away, so the last thing I want is saying something I regret to a client or teammate.</p>]]></description><link>https://blog.hao.dev/my-recent-assertiveness-training</link><guid isPermaLink="false">https://blog.hao.dev/my-recent-assertiveness-training</guid><pubDate>Fri, 27 Jan 2017 18:00:00 GMT</pubDate><content:encoded>&lt;p&gt;I am always trying to be polite, friendly and calm at work. It is called being professional, and it is especially important for people working in professional services. The word professional is in the name kind of gave it away, so the last thing I want is saying something I regret to a client or teammate.&lt;/p&gt;&lt;p&gt;Being professional all the time can be challenging. Everyone has bad days, there are days I feel tired or impatient, and I am always worried that my actions might appear aggressive to others. In my head, I might be saying something reasonable, but other people might see it as I&apos;m snapping at them when they asked a legitimate question. Being in professional services, the first thing to learn is saying no. The client always wants much more than what the team is capable of delivering. Not knowing when to say no is very harmful both to the customer and the team. The client will be disappointed later on when the project can&apos;t be delivered, and the team will feel stressed when the amount of work just keeps piling up.&lt;/p&gt;
&lt;h2&gt;What is assertiveness?&lt;/h2&gt;
&lt;p&gt;I searched &quot;assertiveness&quot; on Google, and this is one of the better definition I got back.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Assertiveness means standing up for my personal rights - expressing thoughts, feelings and beliefs in direct, honest and appropriate ways.&quot; - skillsyouneed.com&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Before attending the training, I saw assertive as being more confident and less passive. It turns out that being less aggressive is just as important.&lt;/p&gt;
&lt;p&gt;My definition of assertiveness now would be &quot;controlling emotions, focusing more on the goal. Achieve it using fair, friendly and neutral language while sounding confident&quot;.&lt;/p&gt;
&lt;h2&gt;Some key points I learned&lt;/h2&gt;
&lt;h3&gt;Do not let the other person dictate your behaviour or thoughts&lt;/h3&gt;
&lt;p&gt;I often find myself distracted by other people&apos;s comment or action. When other gets angry with me, I would also lose patience because I think I&apos;m right. When I&apos;m having an argument with others, they could quickly change the topic to something else by raising some points which I am eager to challenge. For example, when people accuse me of things I quickly become defensive and start questioning them. This leads to the fact I stop focusing on the goal and will easily get tricked into talking about something else.&lt;/p&gt;
&lt;p&gt;Being assertive means that I know what I want to achieve and focus on the goal. When people try to draw me away from the conversation, the best thing is to pause and use repetition assertion to highlight the points you are trying to make. No matter what they are saying, that can wait. Solve one problem at a time is always better than mixing more issues into the ball of mess.&lt;/p&gt;
&lt;h3&gt;Focus on the positive&lt;/h3&gt;
&lt;p&gt;To be assertive, having a clear head is the key. For example, before attending a meeting, I might already be thinking some people might be speaking a lot and complaining about everything. With this kind of negative inner dialogue in my head, it is easy to say something rude without realising. What I should do here is take all these negative dialogues and try to think about the positives. Instead of seeing the person complaining as annoying, I could see it as the person caring about what they are doing, and they have a lot of ideas on how to improve it. With this kind of positive inner dialogue, when things occur in the meeting my use of words will be positive and supportive.&lt;/p&gt;
&lt;p&gt;Being assertive is tough to do, and everyone will find it difficult at first. It suggests I should disagree with myself, even if I were able to change the thought into a positive thought the whole process is still very tiring. Have you wondered why meditation helps people to remain calm and have a more positive view of life? Well, it could just be that when people meditate, they think about the negative thoughts. During the process, they can take a step back and see it from another perspective without making biased assumptions and judgements. Maybe it is time for all of us to start doing some meditation in this busy and stressful time of our lives.&lt;/p&gt;
&lt;h3&gt;Mirror, match, pace &amp;#x26; lead&lt;/h3&gt;
&lt;p&gt;When approaching difficult people, this technique is one of the ways to start building the relationship. The method can be especially useful when the other person is being difficult with you (e.g. not showing much respect).&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If the someone is sitting down having a conversation with me, the first step mirrors the person by slowly adapting to their posture.&lt;/li&gt;
&lt;li&gt;When they change, try to match their position and then pace them as they change. Be careful to maintain the respect for the other people throughout the process.&lt;/li&gt;
&lt;li&gt;After a period, start taking the initiative by start changing my position.&lt;/li&gt;
&lt;li&gt;If the mirror/matching is strong enough, the other person starts copying my position changes. Now, I can lead the other person to the direction I wish.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Maintain the relationship&lt;/h3&gt;
&lt;p&gt;Being assertive is not just about speaking my mind and coming out directly with how I feel. Even though that is what people should do if they find themselves very passive. The important thing here is finding the line between honest and rude. Because one will strengthen a relationship and the other will chip away the foundation.&lt;/p&gt;
&lt;p&gt;When speaking to people, it is important to get my points across. But at the same time, I should be asking the other person about what they want and seek clarification. If a person is trying to tell me something, there are the reason behind it, and without understanding the actual cause, the real problem will never be resolved.&lt;/p&gt;
&lt;p&gt;One of the most important things I learned about being assertive is that while being assertive, I can still be respectful and polite.&lt;/p&gt;
&lt;h3&gt;Build up confidence&lt;/h3&gt;
&lt;p&gt;The first thing is to understand rights belongs to everyone, if someone can say or act a certain way to me, then I should have the same right to do the same with them, and vice versa. If the right only works unidirectional, then something is wrong here. If I think I have more right than other people, then I&apos;m probably not being fair to them and acting in a not assertive way towards them. On the other hand, if I believe someone (e.g. my manager) has more right than me. Then my behaviour will change around him/her, most likely acting passively in conflicting situations.&lt;/p&gt;
&lt;p&gt;One way to build up confidence when in a passive situation is by taking pauses, and let the other person speak. Pausing makes the other person feel respected, and give me to take a step back as well as get a chance to think about the conversation.&lt;/p&gt;
&lt;p&gt;If I am in a position I think the other person is being too passive; I could empathise more with them when they speak. Asking open questions such as &quot;what are your thoughts on…?&quot; should also help to boost their confidence.&lt;/p&gt;
&lt;h2&gt;Techniques&lt;/h2&gt;
&lt;h3&gt;Challenging beliefs&lt;/h3&gt;
&lt;p&gt;I have often heard people say things like &quot;I NEVER want to...&quot; or &quot;ALL of them are...&quot;. When I want to challenge people speaking in this kind of certainty, this is the technique to use.&lt;/p&gt;
&lt;p&gt;For example, what if someone said to me &quot;ALL cats are lazy&quot;?&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Is the statement accurate today?&lt;/li&gt;
&lt;li&gt;Does it contain generalisation?&lt;/li&gt;
&lt;li&gt;Is it the truth or did the person just stated their assumption/interpretation as fact?&lt;/li&gt;
&lt;li&gt;Is there any counter example where the statement does not apply?&lt;/li&gt;
&lt;li&gt;If I can&apos;t think of any counterexample, can the statement be addressed with development?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;No one knows everything; it is best to avoid saying things that are impossible to backtrack.&lt;/p&gt;
&lt;h3&gt;Say NO&lt;/h3&gt;
&lt;p&gt;The biggest problem with passive people is how much they want to avoid saying no to someone. When someone asks them to do something, they will agree to do it no matter how much they didn&apos;t want to do it.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The biggest lesson is to know it is ok to say no. Just come out directly and state that you won&apos;t be doing it. Try to avoid using nice padding words such as &quot;I&apos;d love to but...&quot; or &quot;I&apos;m so sorry, but...&quot;.&lt;/li&gt;
&lt;li&gt;The most respectful thing to do here is to state the reason and make it concise. People will understand if it is a valid reason.&lt;/li&gt;
&lt;li&gt;It is also useful to empathise, to ensure the relationship isn&apos;t broken by showing understanding toward the other person.&lt;/li&gt;
&lt;li&gt;The final useful thing to do trying to give suggestions, maybe there are other people or ways to achieve the same end goal.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the other person doesn&apos;t take the NO very well, just repeat your reasons and empathise with them. Don&apos;t get dictated by their behaviours and stay assertive.&lt;/p&gt;
&lt;h3&gt;Dealing with unwanted behaviour&lt;/h3&gt;
&lt;p&gt;If someone is doing something that is offensive to me. The assertive thing to do is try to talk to them in private and raise the issue. Use the DEESC model to deal with this kind of behaviour.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Describe the situation – state the facts which cannot be challenged, let them know the situation using neutral language.&lt;/li&gt;
&lt;li&gt;Express feelings - let the person know how much the issue affected my feeling.&lt;/li&gt;
&lt;li&gt;Empathise - show some understanding, so they don&apos;t feel suffocated and turns aggressive by acting defensive. The goal here is to give them some room to continue the conversation in a gentle manner.&lt;/li&gt;
&lt;li&gt;Specify a solution – explain what should happen in the future, so the person can easily understand what to avoid next time and never do the same thing again.&lt;/li&gt;
&lt;li&gt;Clarify consequences - if the same issue happens again in the future, there are two ways forward. Using the carrot/stick method, either let them know how much you value the relationship so that they will stop. The other way is to threaten with some undesirable consequences which should hopefully prevent the same behaviour appearing again.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Constructive disagreement&lt;/h3&gt;
&lt;p&gt;When speaking with an unreasonable stakeholder, here is how to raise a disagreement. This could be making statements about your team&apos;s &quot;poor efficiency&quot;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pause - Let the person speak their mind, it is important not to jump to conclusions before fully understanding the problem.&lt;/li&gt;
&lt;li&gt;Ask questions - find the people believe and values, then it will be easy to understand why he/she is acting this way.&lt;/li&gt;
&lt;li&gt;Summarise - extract the key points from their explanation and try to word it into a more assertive description.&lt;/li&gt;
&lt;li&gt;Empathise - show understanding towards what was said and find common grounds which both people agrees.&lt;/li&gt;
&lt;li&gt;State reservations/concerns/issues/points - lead the conversation, so the key points are covered. It could just be that the stakeholder lacked the full picture and misunderstood the situation.&lt;/li&gt;
&lt;li&gt;Make suggestions to move the conversation on and find solutions to solve the client&apos;s problem.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Some final thoughts&lt;/h2&gt;
&lt;p&gt;Like most soft skills, this takes the time to build up the experience. But this training helped me to see everything from a different perspective. Even though I don&apos;t agree everyone should be assertive all the time, it is a useful skill to have when faced with challenging situations.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Migrating blog to Hexo]]></title><description><![CDATA[<p>After using <a href="https://github.com/tryghost/ghost" target="_blank" rel="nofollow noopener noreferrer">Ghost</a> blog for over a year, I decided to switch to <a href="https://hexo.io/" target="_blank" rel="nofollow noopener noreferrer">Hexo</a>. Here's a high level comparison framework the two very different blog framework.</p>]]></description><link>https://blog.hao.dev/migrating-blog-to-hexo</link><guid isPermaLink="false">https://blog.hao.dev/migrating-blog-to-hexo</guid><pubDate>Mon, 23 Jan 2017 17:02:00 GMT</pubDate><content:encoded>&lt;p&gt;After using &lt;a href=&quot;https://github.com/tryghost/ghost&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Ghost&lt;/a&gt; blog for over a year, I decided to switch to &lt;a href=&quot;https://hexo.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Hexo&lt;/a&gt;. Here&apos;s a high level comparison framework the two very different blog framework.&lt;/p&gt;&lt;h2&gt;Ghost V.S. Hexo&lt;/h2&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Hexo is a static website generator, whereas Ghost is a fully functional blog website that allows creating drafts directly from the blog website. Since it is a fully functional website, I expected Ghost to be more complicated to install, however, I was surprised to find that the installation was very similar. With Ghost, all it takes is downloading it from the Ghost repo on Github and then extract the content from the zip file. Hexo is a bit more technical and requires running a few NPM commands in the terminal. Since I am using NPM for most of my projects, it hardly makes any difference for me.&lt;/p&gt;
&lt;h3&gt;Configuration and running&lt;/h3&gt;
&lt;p&gt;There is no clear winner when it comes to configuration and running the blog. Both are good for different things and not great at both. Ghost was super easy to set up due to the straightforward configurations, however, since it requires a server to host the web content the setup takes more time (e.g. spin up new server and installing software on it). Just remember it is not always necessary to install Ghost yourself, for example sites like DigitalOcean could deploy a Ghost blog with just a few clicks. The reason why I decided to migrate to Hexo is the nice compatibility with Github Pages. It means I can essentially host my blog on Github for free. So getting it up and running was as easy as running one command in the terminal. But the downside is that there were a lot of things to configure, and most of useful functionalities doesn&apos;t even have a default value e.g. Language.&lt;/p&gt;
&lt;h3&gt;UI and themes&lt;/h3&gt;
&lt;p&gt;Both frameworks support 3rd party themes and have similar number of resources to choose from. I personally don&apos;t have a preference when it comes to this, and even if none of the themes makes you happy there&apos;s always the option to create one yourself.&lt;/p&gt;
&lt;h3&gt;Functionalities&lt;/h3&gt;
&lt;p&gt;When it comes to functionalities, I think Ghost wins hands down. Not only the fact it supports multiple users, but the blog writing and publishing features feels fantastic to use as well. The split view of Markdown and rendered view is an amazing idea that doesn&apos;t exist in other frameworks I came across. It strikes the perfect balance between &quot;focus on writing&quot; and &quot;focus on the end result&quot;. Image upload, draft auto-save and other functionalities makes writing blog posts fun to write. With Hexo, there&apos;s only one thing to do which is write it in a IDE or TextEditor. Uploading images and videos also involves more manual steps, definitely less enjoyable compared to Ghost.&lt;/p&gt;
&lt;p&gt;For more advanced features such adding search, comments or changing themes I actually find Hexo much easier to customize. Not only because the configuration is in the same place, but since there&apos;s no server involved deployment is also easier. With Ghost, most of the time I had to SSH into the server and modify a bunch of files to get the same result.&lt;/p&gt;
&lt;p&gt;Publishing blogs can&apos;t be easier on Ghost, with a click of a button it can be published or unpublished. For Hexo, it takes a few more steps (commit changes &amp;#x26; deploy to Github Pages) and often requires the author to be using one machine due to the dependency on NPM set up and etc.&lt;/p&gt;
&lt;h3&gt;Long term support and upgrade&lt;/h3&gt;
&lt;p&gt;Both frameworks has a community behind it. As of now, Ghost has 21,720 Stars and 5,160 forks on Github whereas Hexo only has 14,282 Stars and 2,206 forks. This makes Ghost the clear winner. So why did I switch to a less &quot;popular&quot; framework? During the time I have been using Ghost, there has been a few updates and the only way to install them is by SSH into the server and download/unzip the newer version of Ghost and migrate the data across. This is not a good experience and definitely gets annoying after a few times. The constant notification reminder in the admin panel also doesn&apos;t help. For Hexo, it would be as easy as running &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt; in the terminal.&lt;/p&gt;
&lt;h3&gt;Final thoughts&lt;/h3&gt;
&lt;p&gt;It has only been a few days with Hexo, and already I am enjoying it more than Ghost. Hexo is probably not suitable for non-technical people out there. But if you are a developer or comes from a technical background (basically knows how NPM and Github works) then I encourage you to give this a try. After the switch over, I don&apos;t ever have to worry about my server being taken offline and the hosting will always be free :)&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Scrum development from a developer's point of view]]></title><description><![CDATA[<p>Scrum development is great, it allows iterative development and much faster feedback loops. The concept is nothing new to a lot of people, and the adoption rate has been increasing. But I have yet to work in a team where I think we have achieved the maximum efficiency, in fact, most of the time we are far from it. The concept of Scrum seems easy enough, so why it is so hard to do it right?</p>]]></description><link>https://blog.hao.dev/scrum-development-from-a-developer-s-point-of-view</link><guid isPermaLink="false">https://blog.hao.dev/scrum-development-from-a-developer-s-point-of-view</guid><pubDate>Thu, 29 Dec 2016 22:45:00 GMT</pubDate><content:encoded>&lt;p&gt;Scrum development is great, it allows iterative development and much faster feedback loops. The concept is nothing new to a lot of people, and the adoption rate has been increasing. But I have yet to work in a team where I think we have achieved the maximum efficiency, in fact, most of the time we are far from it. The concept of Scrum seems easy enough, so why it is so hard to do it right?&lt;/p&gt;&lt;p&gt;I have used Scrum in more than 6 projects, some are over 13 months long and others could be as short as a few weeks. I even have the Scrum Mater certificate from Scrum.org, so the concept is not new to me. People always talk about a project from a business or a Product Owner&apos;s point of view, here I want to bring a new perspective and talk about some of the issues I have experienced as a developer.&lt;/p&gt;
&lt;h4&gt;All about the product and NOT the project&lt;/h4&gt;
&lt;p&gt;I think that people need to focus on the product, and not the work itself. Waterfall development methodology is all about the &lt;strong&gt;project&lt;/strong&gt;, the goal is to produce something that is closest to the requirement spec as possible. Once the project is finished then that is it, the team built it is no longer responsible for the product. On the other hand, agile development focuses on the &lt;strong&gt;product&lt;/strong&gt;. The development team must care about the product, suggesting ways to improve it over time. A good development team will fix issues that are not captured in the business requirement, they will also actively find and report issues about the product.&lt;/p&gt;
&lt;h4&gt;Less can be more&lt;/h4&gt;
&lt;p&gt;A team consists of fewer people might have a clearer focus and can self-manage much better than a bigger team. I have worked on a project where the two of us managed to rewrite the front end of a web application is a few days. For a different project, the team of around 5 developers achieved almost nothing for weeks. Skills and experience obviously come into play here, but I think the desire to do something and do it well is also a big factor. With a smaller team, it is much easier for the developers to come to the same understanding than a big team.&lt;/p&gt;
&lt;h4&gt;More isn&apos;t always more&lt;/h4&gt;
&lt;p&gt;A development team is always better to be kept consistent, adding or removing people is going to impact the velocity, always! Just because the burndown chart shows the team is working just as well, the code quality and the overall maintainability of the product will say otherwise. It is a common problem to have a set deadline for projects, this means rather than pushing back deadline business will decide to increase the team&apos;s size to increase velocity. But business doesn&apos;t understand that if a team isn&apos;t delivering enough, allocating 5 more developers is not going to help. If anything, changing the team is going to decrease velocity due to the extra training time spent bringing the new joiners up-to-speed. Sure, it might be a velocity increase a few months down the line but it can&apos;t be used to fix a short term goal.&lt;/p&gt;
&lt;h4&gt;Code quality&lt;/h4&gt;
&lt;p&gt;When working in a team of 3 - 15 people, it is hard to make sure everyone has the right skills, experience and same ways of working. This is one of the problems I have seen with some of the teams I have been in. As a developer, when there are one or more people producing crappy code, it drags down the quality of the product overall. It introduces bugs, makes the product more difficult to maintain and just simply makes developing the product less enjoyable. Nobody enjoys working on a product that they don&apos;t like.&lt;/p&gt;
&lt;h4&gt;Good Product Owner and a good backlog&lt;/h4&gt;
&lt;p&gt;A good product owner (PO) is someone who is always accessible to the team, he/she should be there to answers questions as they come up. Waiting till the next day is a sign that the PO should spend more time with the development team. The PO should be actively shouldering any blames made to the product, as he/she is the sole person responsible for the product. In order to avoid blades, the PO should clearly understand the business requirements and break them down into tasks and user stories. This leads on to my next point, PO is also responsible for creating the product backlog.&lt;/p&gt;
&lt;p&gt;All future changes must be captured and stored in the backlog, tasks and user stories should be clear to the development team. It sounds straightforward, but the backlog needs to be managed, maintained and updated just like the product&apos;s code base. This is always something that is missed and causes dragged out arguments in planning or refinement sessions.&lt;/p&gt;
&lt;h4&gt;Testing or no testing?&lt;/h4&gt;
&lt;p&gt;A good development team will always have automated testing, this includes unit, integration and end-to-end tests. The team should find a balance between the different type of tests, and focus on writing the least number of quality tests as possible. In a recent project, we have gone overboard with regression tests, not only the tests takes hours to run locally but also means write tests takes 2/3 of the total development time. I can say for sure that is a terrible place to be, so definitely try to avoid the mistakes we had made.&lt;/p&gt;
&lt;h4&gt;Why Scrum, why not Kanban?&lt;/h4&gt;
&lt;p&gt;I personally prefer Kanban over Scrum, because Scrum has all these scrum ceremonies to follow whereas Kanban is extremely simple. With Scrum, new teams often miss important points and use it wrongly, and for a more experienced team, it is easy to forget about the big picture and get caught up in scrum ceremonies like Sprint Retrospective. It is extremely difficult to find a good balance, and all the ceremonies could become a distraction for the team and take away the more important focus on the product they are trying to build. I am certainly not the expert in this field, but I think for a new development team picking up Scrum or Kanban will take the same amount of time. With Kanban, there&apos;s a much clearer focus on the product.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Google Sheets as a service]]></title><description><![CDATA[<p>The most difficult choice to make when creating a simple web application is not choosing the right framework or colour scheme (for any backend devs out there). Instead, the struggle comes from deciding if a backend is required or necessary. A lot of the time, it is hard to justify setting up a backend service for a simple web application. More times than not, the only thing I really need is one or two simple APIs to bounce some data around. For that, I don't really want to spin up a new EC2 instance on AWS, create a backend, setting up a database and etc.</p>]]></description><link>https://blog.hao.dev/google-sheets-as-a-service</link><guid isPermaLink="false">https://blog.hao.dev/google-sheets-as-a-service</guid><pubDate>Sun, 25 Dec 2016 22:44:00 GMT</pubDate><content:encoded>&lt;p&gt;The most difficult choice to make when creating a simple web application is not choosing the right framework or colour scheme (for any backend devs out there). Instead, the struggle comes from deciding if a backend is required or necessary. A lot of the time, it is hard to justify setting up a backend service for a simple web application. More times than not, the only thing I really need is one or two simple APIs to bounce some data around. For that, I don&apos;t really want to spin up a new EC2 instance on AWS, create a backend, setting up a database and etc.&lt;/p&gt;&lt;p&gt;There are plenty of services out there which already do this kind of stuff I know, AWS has Lambda for serverless services. Even with Lambda functions, I still need a database to store any data. The closest service I have found is called &lt;a href=&quot;https://orchestrate.io/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Orchestrate&lt;/a&gt;. It essentially lets me run a database then using different API calls I can query different data from it. The downside of both of these solutions is that they are paid services, sure it might not cost a whole lot but no doubt it is a bad way to implement open source projects. With things tied to my bank account, it then can&apos;t be shared publicly and the open source community won&apos;t be able to contribute to it. So this is where Google Sheets comes into play!&lt;/p&gt;
&lt;p&gt;I have been a massive fan of the entire Google Doc suite, but I only found out recently that there are publics APIs for querying data from any published Google Sheet documents. Sounds amazing right? If I need an API to return some data, I can just create a Google Sheet document then populate it with whatever data I need. Compared to my previous method, which involved storing data as a JavaScript object in the application, using Google Sheets means data and source code can be decoupled. Another thing to point out is using Google APIs, all the request balancing and performance are passed to Google there is no additional work on my end.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Note: For the purpose of this blog, I&apos;m going to use the non-authenticated API which required the document to be publicly viewable in order to request data from it (bypass authentication). So if the project you are working on has sensitive data, I would recommend that you use the authenticated implementation as suggested &lt;a href=&quot;https://developers.google.com/gdata/samples/spreadsheet_sample&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt; by google.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;How to request data from Google Sheet document&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;1. Publish the document&lt;/strong&gt;&lt;br&gt;
On the Google Sheet document, click on &lt;code class=&quot;language-text&quot;&gt;File&lt;/code&gt; then &lt;code class=&quot;language-text&quot;&gt;Publish to the web...&lt;/code&gt;. Once the popup opens, just click on the blue &lt;code class=&quot;language-text&quot;&gt;Publish&lt;/code&gt; button. Here is my example &lt;a href=&quot;https://docs.google.com/spreadsheets/d/1MYzZjtsujLXH43F_iQl242OV045UahkENWkv0nF83Qo/edit?usp=sharing&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Google Sheet document&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. Find out the document ID and worksheet ID&lt;/strong&gt;&lt;br&gt;
Once published, copy the shareable link from the same popup. Now open it in a new tab, you can see it is an HTML version of the same document. There are a bunch of formats you can get it in. I personally recommend JSON, which is not on the list. Don&apos;t worry this is completely fine, just leave the data type as &lt;code class=&quot;language-text&quot;&gt;Web page&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. Return data as JSON&lt;/strong&gt;&lt;br&gt;
Use the following API to request data back as JSON, just replace &lt;code class=&quot;language-text&quot;&gt;DOCUMENT_ID&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;SHEET_ID&lt;/code&gt; respectively with your own.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://spreadsheets.google.com/feeds/cells/DOCUMENT_ID/SHEET_ID/public/basic?alt=json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;DOCUMENT_ID&lt;/code&gt; is the key of the spreadsheet you want to retrieve, it can be found in the URL. In my case, it is docs.google.com/spreadsheets/d/==1MYzZjtsujLXH43F_iQl242OV045UahkENWkv0nF83Qo==/edit#gid=327263911.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;SHEET_ID&lt;/code&gt; is the positional or unique identifier of the worksheet, the default ID is &lt;code class=&quot;language-text&quot;&gt;od6&lt;/code&gt; for default sheet name &lt;code class=&quot;language-text&quot;&gt;Sheet1&lt;/code&gt;. If you don&apos;t know what your &lt;code class=&quot;language-text&quot;&gt;SHEET_ID&lt;/code&gt; is, then it is worth using the following API to dig through and find it.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;https://spreadsheets.google.com/feeds/worksheets/DOCUMENT_ID/private/full&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The returned data is a massive JSON object, unfortunately, I had to write my own logic to extract the relevant data from it. Depending on how the sheet is laid out the code implementation to extract it will obviously differ. For this reason, possibly it is best to only consider Google Sheet as short term or temporary backend service.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. Additional APIs to insert, update, delete data from the document&lt;/strong&gt;&lt;br&gt;
For anything else more advanced, please refer to the official Google API &lt;a href=&quot;https://developers.google.com/sheets/api/reference/rest/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;documentation&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using vue-cli (vuejs) with Github Pages]]></title><description><![CDATA[<p>Vue.js is a fantastic JavaScript framework, I have been using it to build small projects in its very early stages. The fact you can set up your projects in so many different ways is amazing, there are ways to set up a quick hack project or to build something is maintenance ready. One of the best ways to set up Vue properly is using Vue-CLI.</p>]]></description><link>https://blog.hao.dev/using-vue-cli-vuejs-with-github-pages</link><guid isPermaLink="false">https://blog.hao.dev/using-vue-cli-vuejs-with-github-pages</guid><pubDate>Sat, 24 Dec 2016 22:43:00 GMT</pubDate><content:encoded>&lt;p&gt;Vue.js is a fantastic JavaScript framework, I have been using it to build small projects in its very early stages. The fact you can set up your projects in so many different ways is amazing, there are ways to set up a quick hack project or to build something is maintenance ready. One of the best ways to set up Vue properly is using Vue-CLI.&lt;/p&gt;&lt;p&gt;Recently, I have found some issues with the Vue-CLI when I tried to use it with Github Pages (an amazing way to set up a quick prototype, I&apos;ll prob write more about it in another blog).&lt;/p&gt;
&lt;h4&gt;Problem 1: Builds to &lt;code class=&quot;language-text&quot;&gt;/dist&lt;/code&gt; folder instead Github prefered &lt;code class=&quot;language-text&quot;&gt;/docs&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;This turns out to be super easy to fix, simply open &lt;code class=&quot;language-text&quot;&gt;/config/index.js&lt;/code&gt; file and find the following lines.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;...
index: path.resolve(__dirname, &apos;../dist/index.html&apos;),
assetsRoot: path.resolve(__dirname, &apos;../dist&apos;),
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Then change them to the following:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;...
index: path.resolve(__dirname, &apos;../docs/index.html&apos;),
assetsRoot: path.resolve(__dirname, &apos;../docs&apos;),
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h4&gt;Problem 2: File path is broken and shows blank page on Github Pages&lt;/h4&gt;
&lt;p&gt;After fixing the first problem, pushing the changes to master. Then load up the relative URL, then I realised there is still nothing being displayed. Here are the console errors:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3kAdgeW0dOaCK8sAMyGiUi/0f21817e4343f5494d969a0eb988a4e7/browser-console.png&quot; alt=&quot;Browser Console Error&quot;&gt; &lt;/p&gt;
&lt;p&gt;Looking at the build info in the terminal, it also says the build directory should be served over an HTTP server.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/KjWBMsYi4w8yQCs2gKCuG/77167e9ba4c7758a3b3c1cfdc4035838/terminal.png&quot; alt=&quot;terminal&quot;&gt;)&lt;/p&gt;
&lt;p&gt;After looking around at the config more, I also found a relatively easy fix. Again open up &lt;code class=&quot;language-text&quot;&gt;/config/index.js&lt;/code&gt; file and find the following line.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;...
assetsPublicPath: &apos;/&apos;,
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now just change the absolute path to a relative path, then voila, everything should work on Github Pages as expected.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;...
assetsPublicPath: &apos;./&apos;,
...&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Also feel free to remove or comment out the warning in the terminal show above, since we have now fixed the problem. Within &lt;code class=&quot;language-text&quot;&gt;build/build.js&lt;/code&gt;, the following snippet is responsible for showing the warning.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;console.log(
  &apos;  Tip:\n&apos; +
  &apos;  Built files are meant to be served over an HTTP server.\n&apos; +
  &apos;  Opening index.html over file:// won\&apos;t work.\n&apos;
)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Web security - OWASP top 10 vulnerabilities]]></title><description><![CDATA[<p>Open Web Application Security Project (OWASP) is a non-profit organisation that was driven by a community which wishes to make the world wide web a safer place. The latest top 10 document can found <a href="https://www.owasp.org/index.php/Top10#OWASP_Top_10_for_2013" target="_blank" rel="nofollow noopener noreferrer">here</a>. As you can see it is created in 2013, so many of the issues would have been addressed already by new technologies. In this blog, we will be explaining the top 10 vulnerabilities and how they affect the technology today.</p>]]></description><link>https://blog.hao.dev/web-security-owasp-top-10-vulnerabilities</link><guid isPermaLink="false">https://blog.hao.dev/web-security-owasp-top-10-vulnerabilities</guid><pubDate>Thu, 22 Dec 2016 22:41:00 GMT</pubDate><content:encoded>&lt;p&gt;Open Web Application Security Project (OWASP) is a non-profit organisation that was driven by a community which wishes to make the world wide web a safer place. The latest top 10 document can found &lt;a href=&quot;https://www.owasp.org/index.php/Top10#OWASP_Top_10_for_2013&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;. As you can see it is created in 2013, so many of the issues would have been addressed already by new technologies. In this blog, we will be explaining the top 10 vulnerabilities and how they affect the technology today.&lt;/p&gt;&lt;h3&gt;Injection&lt;/h3&gt;
&lt;p&gt;This is something we hear about all the time, mostly known for websites written in PHP that uses MySQL as its database. Since the combination was fairly old technology and it did not have built-in defences against such simple attack. By typing SQL queries into inputs and URL parameters, attackers are able to perform unauthorised actions such as return other user&apos;s data or even drop (delete) production databases.&lt;/p&gt;
&lt;p&gt;The fix for this issue wasn&apos;t difficult. Any website can prevent this by adding type checks on form inputs and URL parameters to ensure injection was not possible. An even easier fix would be to choose or a database management system that has a built-in defence for SQL injection e.g. PostgreSQL. The problem was never with the difficulty of the issue, instead, it was just developers lacked the understanding of this vulnerability or was too lazy to implement checks for 100% of the code base. As a result, a single point of failure could put the entire website risk.&lt;/p&gt;
&lt;p&gt;This issue is still an important problem today, this is because there are still many legacy systems out there. If a web was built on today&apos;s frameworks, I feel this wouldn&apos;t be a problem at all. Modern frameworks are very good at preventing the developer from write code that is prone to this attack.&lt;/p&gt;
&lt;h3&gt;Authentication/Session management&lt;/h3&gt;
&lt;p&gt;Many websites require a user to login in order to access more features on a website. To avoid constantly asking the user to type their username and password, a session is used to tell the website the user is already logged in and who he/she is. This vulnerability is when attackers steal user sessions and pretend that they are authorised and have access to information they shouldn&apos;t have.&lt;/p&gt;
&lt;p&gt;This is a much broader issue since not all website is implemented the same and depending on how it is created the vulnerability could be different. In some cases, the problem could be that a session ID is sent as part of a link that allows anyone to login as the user. There are also ways to steals people&apos;s authentication cookie by sniffing over insecure networks. The best way to prevent this is to always ensure session cookie has a timeout period and is regenerated upon every authentication.&lt;/p&gt;
&lt;p&gt;This issue has not changed since 2013 since the front end session management and authentication has hardly changed. Sure, there are ways to ensure the websites&apos; security. But the fundamental issue never went away. If the attacker is clever enough and the user is silly enough, there are plenty of ways for the attacker to steal their session e.g. Cross Site Scripting.&lt;/p&gt;
&lt;h3&gt;Cross Site Scripting (XSS)&lt;/h3&gt;
&lt;p&gt;Used to be a popular method to embed snippets of code on a legitimate website that could take a user to fake websites and trick them into typing in their credentials so it can be captured by the attacker. There are also many other reasons to perform this type of attack. A simple example of this could be the attacker noticed that a segment of the URL parameter is mapped to a DOM element unconditionally. Thus, by changing the URL parameter to a snippet of JavaScript code could essentially manipulate the DOM element to do as the heart desires.&lt;/p&gt;
&lt;p&gt;In modern frameworks, especially front end, there are very little ways to deploy this kind of attack. Frameworks such as Angular, React and etc does not rely on URL parameters as much as jQuery websites do. These frameworks are also much tighter on URL parameters and routeing, it doesn&apos;t leave any room for error. On the other hand, modern frameworks also utilise two-way data binding, so gone are the days where values have to be passed as a URL parameter to a DOM element.&lt;/p&gt;
&lt;h3&gt;Direct Object References&lt;/h3&gt;
&lt;p&gt;This kind of attack is to do with authorisation, in older websites database record IDs are referred to in the URL parameter (again, yes). If security is not in place, user or an attacker could simply increment the ID value and get someone else&apos;s data. Ideally, there should be a check which only allows the user to request their own data. Surprisingly, a few month back a colleague of mine actually found this kind of issue on a live production website for a well-known company. It is pretty much common sense, but yes companies often make this kind of mistakes.&lt;/p&gt;
&lt;p&gt;Unlike older websites, newer sites use RESTful APIs to make requests to the server and the URL parameters are hidden from the browser address bar. But the problem is still there, an attacker could still fake a request (a tiny bit more work than altering the URL) to the server asking for someone else&apos;s data. So correct security checks must be in place, this is extremely important for public facing APIs since everyone on the internet could access them.&lt;/p&gt;
&lt;h3&gt;Security Misconfiguration&lt;/h3&gt;
&lt;p&gt;There are new system and software vulnerabilities are found all the time, thus it is very important to keep everything up-to-date. Companies, especially larger companies, are always reluctant to upgrade because new changes could break their software and cause incompatible issues. I mean look how many times companies paid Microsoft to not drop support for Windows XP when windows 8 was already out. Imagine the vulnerabilities on a system that is almost 13 years old. Other ways to reduce risks of vulnerabilities are to disable ports, services and accounts if they are not being utilised. Speak of accounts, ensure all default passwords are changed to something more secure, e.g. router or database passwords.&lt;/p&gt;
&lt;p&gt;There are probably a million other things to do and your system might still be vulnerable. But here are 3 areas to start securing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Turn off features that are not needed and apply the &quot;principle of least privilege&quot;&lt;/li&gt;
&lt;li&gt;Change default settings, and ensure production-ready (turn off debugging, and limit admin access)&lt;/li&gt;
&lt;li&gt;Always try to stay up-to-date in terms of tools, software and systems&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Sensitive Data Exposure&lt;/h3&gt;
&lt;p&gt;This issue covers the lack of SSL (HTTPS) implementation of websites, using secure connection prevents a lot of issues such as &quot;Man in the middle&quot; attack. The password is obviously one of the most important user data, so the lack of encryptions could lead to database table being decrypted and leaked after one point of failure e.g. Inject. If the encryption is strong enough, even if the data is stolen the attacker can&apos;t do anything with it (a strong encryption could take hundreds of years to decrypt). It is almost silly not to use strong algorithms to ensure the security.&lt;/p&gt;
&lt;p&gt;Companies often try to capture as much of user information as possible, what businesses don&apos;t understand is that unless the data adds value recording it simples puts the company more at risk. By capturing fewer data, it might mean a much better end result if the system was compromised and user data was stolen. As a user, I definitely prefer to only have my email address leaked, compared to all of my personal information e.g. date of birth, address and etc. Having more user data might attract more attackers in the first place, since they know they can get all the data they need just by hacking your company. So to both reduce potential damage and cost, only record data that is absolutely necessary.&lt;/p&gt;
&lt;p&gt;HTTPS is currently being pushed by a lot of companies, Google being one of them. Soon Chrome will start marking websites as insecure if they do not have HTTPS connection. &lt;a href=&quot;https://letsencrypt.org/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Let&apos;s Encrpty&lt;/a&gt; also provides free SSL certificates for any website for free, again to push for a more secure online experience.&lt;/p&gt;
&lt;h3&gt;Function Level Access Control&lt;/h3&gt;
&lt;p&gt;This issue is similar to the &quot;Direct Object References&quot; section, but just on a more general. It is important to prevent the UI from showing unauthorised functionalities. If a user should not have access to a button don&apos;t just disable it, hide it completely. It is also worth noting just because a page is not navigatable doesn&apos;t mean the attacker can&apos;t find it by looking through the code, so prevent user going to that page altogether in the routeing.&lt;/p&gt;
&lt;p&gt;As mentioned already, it is crucial to perform all the security checks on each and every public facing APIs. This includes checking if the user is authenticated, authorised, has the correct privileges, and etc. It is important to have validation on the front end, but API level testing is more important. Since it is extremely easy to bypass front end validations and checks.&lt;/p&gt;
&lt;h3&gt;Cross-Site Request Forgery (CSRF)&lt;/h3&gt;
&lt;p&gt;This attack is very clever, almost like a puppet master controlling the user to do their biddings. Often, the attacker will trick the user from making requests to important requests (e.g. doing a bank transfer to the attacker). This could be a fake website, this could also be part of an XSS attack.&lt;/p&gt;
&lt;p&gt;To prevent this type of attacks, there are a few things we can do:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Anti-forgery tokens, that is generated randomly instead of sending the same token every time&lt;/li&gt;
&lt;li&gt;Valid requests only from known origins and referrers, fraud detection patterns&lt;/li&gt;
&lt;li&gt;Some browsers already have built-in defences for this e.g. Chrome prevents cross-origin requests&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Using Components with Known Vulnerabilities&lt;/h3&gt;
&lt;p&gt;Component and version could be discovered by studying the request headers, file name patterns and etc. We already know that the components have vulnerabilities, so it is a lost battle right? Kind of, it is pretty difficult to defend against this kind of issues. The best thing to do is to update to a newer version with a fix as soon as possible or to move away from unsafe components.&lt;/p&gt;
&lt;h3&gt;Unvalidated Redirects and Forwards&lt;/h3&gt;
&lt;p&gt;You probably already noticed as we move down the list, the amount of description and text is decreasing. This is because attacks such as this one have a much less impact than issues such as SQL injection. The concept of this attack is when attacker changes redirect links on a website, so they are redirected to other malicious websites. At this point we are assuming, the original website has already been compromised and this attack is simply a way to spread malware to more users. This is so low in terms of importance, companies like Google doesn&apos;t even offer bug bounties for reporting this kind of issue on their sites.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Security is a constant battle, it is also an on-going issue in terms of support and maintenance. As explained already some of the issues are already ageing, and might disappear in a few years when all the frameworks contain built-in support for these type of attacks. At the same time, more issues will appear, for frameworks like Angular its implementation is very predictable thus may allow attackers to think of new ways of attack. To conclude, I feel some of the issues are definitely worth bearing in mind when developing new products. A lot of the issues mentioned above are to do with the backend system and backend development has not changed that much in the past few years. Although new database technology such as MongoDB does not have any vulnerability towards query injection and more websites will be adopting HTTPS, so I guess somethings are changing and for the better too.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Using NPM scripts correctly]]></title><description><![CDATA[<p>Nowadays, most frontend projects and JavaScript projects use NPM (Node Package Manager) to manage dependencies. This is often done through the file <code class="language-text">package.json</code>. This file, as the name extension suggests, contains a JSON object about the project. The follow snippet shows a very simple example of what a <code class="language-text">package.json</code> file might contain, for more information about what each section means <a href="http://browsenpm.org/package.json" target="_blank" rel="nofollow noopener noreferrer">browsenpm</a> will offer a much better explanation. If you have not seen a <code class="language-text">package.json</code> file before, it might be best if you do some quick reading before continuing reading.</p>]]></description><link>https://blog.hao.dev/using-npm-scripts-correctly</link><guid isPermaLink="false">https://blog.hao.dev/using-npm-scripts-correctly</guid><pubDate>Sun, 18 Dec 2016 22:39:00 GMT</pubDate><content:encoded>&lt;p&gt;Nowadays, most frontend projects and JavaScript projects use NPM (Node Package Manager) to manage dependencies. This is often done through the file &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;. This file, as the name extension suggests, contains a JSON object about the project. The follow snippet shows a very simple example of what a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file might contain, for more information about what each section means &lt;a href=&quot;http://browsenpm.org/package.json&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;browsenpm&lt;/a&gt; will offer a much better explanation. If you have not seen a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file before, it might be best if you do some quick reading before continuing reading.&lt;/p&gt;&lt;h3&gt;A little bit of background info on package.json&lt;/h3&gt;
&lt;p&gt;Nowadays, most frontend projects and JavaScript projects use NPM (Node Package Manager) to manage dependencies. This is often done through the file &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt;. This file, as the name extension suggests, contains a JSON object about the project. The follow snippet shows a very simple example of what a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file might contain, for more information about what each section means &lt;a href=&quot;http://browsenpm.org/package.json&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;browsenpm&lt;/a&gt; will offer a much better explanation. If you have not seen a &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file before, it might be best if you do some quick reading before continuing reading.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;package-json-sample&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;version&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.1.0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A sample package.json file&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;main&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;node index.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo \&quot;Error: no test specified\&quot; &amp;amp;&amp;amp; exit 1&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dependencies&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;express&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;^4.13.3&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;repository&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;git&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token property&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;https://github.com/some-url&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;keywords&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token string&quot;&gt;&quot;package&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;json&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;example&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao Dong&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;license&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;MIT&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now we got that out of the way, let&apos;s talk about why in my opinion how to set up projects correctly by using NPM scripts.&lt;/p&gt;
&lt;h3&gt;What are NPM scripts?&lt;/h3&gt;
&lt;p&gt;NPM scripts are actually super customisable and powerful, it is a shame that they are not always being utilised fully/correctly. You can run simple commands to do something like cleaning up projects or more complex chained commands to combine, compile and build projects.&lt;/p&gt;
&lt;p&gt;So how do they work? Within &lt;code class=&quot;language-text&quot;&gt;scripts&lt;/code&gt; object in the example above, there are two NPM commands &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt;. When declared in &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; like this, it means if you navigate to the directory in the terminal and you can execute them by typing &lt;code class=&quot;language-text&quot;&gt;npm run start&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm run test&lt;/code&gt; (&lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;npm test&lt;/code&gt; also works because &lt;code class=&quot;language-text&quot;&gt;start&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;test&lt;/code&gt; are reserved keywords). In our case, &lt;code class=&quot;language-text&quot;&gt;npm start&lt;/code&gt; will start a node app using a file called &lt;code class=&quot;language-text&quot;&gt;index.js&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;npm test&lt;/code&gt; will print out &quot;Error: no test specified&quot; in the terminal.&lt;/p&gt;
&lt;h3&gt;Why should we use it?&lt;/h3&gt;
&lt;h4&gt;1. Using local rather than global NPM packages&lt;/h4&gt;
&lt;p&gt;This is probably the most important reason in my opinion. Tools such as Bower, Gulp and etc requires to be installed as a global NPM package, like most cases in software development global is never good. This causes a number of problems, just imagine:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;You are working on two different projects but they require different versions of the same NPM package (e.g. web driver). If you install it globally, how much of a pain will this be to constantly change between them?&lt;/li&gt;
&lt;li&gt;What if you are on a newer version of a package than your co-workers? Or even worse, what if everyone is a different version to the production environment? When things start failing, it will be hard to track down issues and creates confusion.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;You might ask why do people keep installing NPM packages as global then? Well, if you want to run something like &lt;code class=&quot;language-text&quot;&gt;gulp test&lt;/code&gt; in the terminal Gulp must be installed as a global package, otherwise, &lt;code class=&quot;language-text&quot;&gt;gulp&lt;/code&gt; is undefined.&lt;/p&gt;
&lt;p&gt;How do NPM scripts fix this? Well, we don&apos;t have this issue because NPM script will try to find the relevant packages within &lt;code class=&quot;language-text&quot;&gt;node_modules&lt;/code&gt; directory. This means that I can run &lt;code class=&quot;language-text&quot;&gt;npm test&lt;/code&gt; (assuming the script is &lt;code class=&quot;language-text&quot;&gt;&quot;test&quot;: &quot;gulp test&quot;&lt;/code&gt;), and it will use the locally installed version of Gulp. The local package version should always be defined in the &lt;code class=&quot;language-text&quot;&gt;dependencies&lt;/code&gt; or &lt;code class=&quot;language-text&quot;&gt;devDependencies&lt;/code&gt; objects within &lt;code class=&quot;language-text&quot;&gt;package.json&lt;/code&gt; file. Therefore, everyone will be using the same version of Gulp this way.&lt;/p&gt;
&lt;h4&gt;2. Easy to find&lt;/h4&gt;
&lt;p&gt;Since package.json is used by a lot of tools and it appears in almost every JavaScript project, therefore it is the first place developers will look to understand a project (still important to have a good README file!). So why not put the most important information in the place people will look first?&lt;/p&gt;
&lt;h4&gt;3. Centralise commands&lt;/h4&gt;
&lt;p&gt;Different projects use different JavaScript package managers, bundlers, and task runners. E.g. NPM, Bower, Browserify, Gulp, Grunt, Webpack and many others. Rather than having each of these tools doing something different (e.g. Bower as bundler and Gulp as task runner), why not put the commands in one place and centralise them? NPM scripts can execute commands for any of these tools. If I want to run something in gulp I can just have a script like &lt;code class=&quot;language-text&quot;&gt;&quot;test&quot;: &quot;gulp test&quot;&lt;/code&gt;, then run &lt;code class=&quot;language-text&quot;&gt;npm run test&lt;/code&gt; in the terminal to execute it. So when developing I can just run one command, instead of a whole bunch just to get it to build my project.&lt;/p&gt;
&lt;h4&gt;4. Complex/chained commands&lt;/h4&gt;
&lt;p&gt;As mentioned before NPM will allow chaining commands, if you want to something quite complicated this is super useful. The snippet below shows what a chained command looks like.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;start&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm run command1 &amp;amp;&amp;amp; npm run command2 &amp;amp;&amp;amp; npm run command3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;command1&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo \&quot;Running command 1\&quot; &amp;amp;&amp;amp; exit 1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;command2&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo \&quot;Running command 2\&quot; &amp;amp;&amp;amp; exit 1&quot;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;command3&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;echo \&quot;Running command 3\&quot; &amp;amp;&amp;amp; exit 1&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;After looking at the chained commands you might think all the &lt;code class=&quot;language-text&quot;&gt;npm run&lt;/code&gt; is a bit ugly when chaining scripts. Well, it happens someone already thought of this. There&apos;s a nice package called &lt;a href=&quot;https://www.npmjs.com/package/npm-run-all&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;npm-run-all&lt;/a&gt; which lets you write much nicer chained commands. Using this package, you can write nice chained commands in the next section.&lt;/p&gt;
&lt;h3&gt;The recommended way of using NPM scripts&lt;/h3&gt;
&lt;p&gt;As of now, the follow snippest shows my recommended way of using NPM sripts and structuring projects.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token property&quot;&gt;&quot;scripts&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;dev&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm-run-all build:clean build:html watch:html build:css watch:css watch:js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;npm-run-all build:clean build:html build:css build:js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:clean&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;rm -rf ./docs/*&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cp -r ./src/*.html ./docs/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;watch:html&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;onchange &apos;./src/*.html&apos; -v -- npm run build:html &amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:css&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cp -r ./src/*.css ./docs/&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;watch:css&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;onchange &apos;./src/*.css&apos; -v -- npm run build:css &amp;amp;&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;build:js&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;browserify ./src/app.js -o ./docs/bundle.js&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;watch:js&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;watchify ./src/app.js -o ./docs/bundle.js -v&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;mocha ./test/**/*.js&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The above example is taken from one of my recent project &lt;a href=&quot;https://github.com/csukcc/finance-calc&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Finacial Calc&lt;/a&gt; if you are interested in looking at the folder structures and etc.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Easy ways to mange Email Inbox]]></title><description><![CDATA[<p>Keep a clean Inbox is not an easy task for even the most organised people because there are simply too many spams. There is only so many filters you can set up before it gets really annoying and tedious. Here are just some simple, but useful ways to keep a clean Email Inbox.</p>]]></description><link>https://blog.hao.dev/easy-ways-to-mange-email-inbox</link><guid isPermaLink="false">https://blog.hao.dev/easy-ways-to-mange-email-inbox</guid><pubDate>Mon, 12 Dec 2016 22:37:00 GMT</pubDate><content:encoded>&lt;p&gt;Keep a clean Inbox is not an easy task for even the most organised people because there are simply too many spams. There is only so many filters you can set up before it gets really annoying and tedious. Here are just some simple, but useful ways to keep a clean Email Inbox.&lt;/p&gt;&lt;h3&gt;Unsubscribe from all marketing emails&lt;/h3&gt;
&lt;p&gt;It can get overwhelming very quickly, after signing up to a few online stores. The best thing to do is to change the email settings as soon as the account is created. This way you won&apos;t ever need to worry about it later on, and you won&apos;t get into the awkward situation where you need to reset your password first before unsubscribe if you end up forgetting it.&lt;/p&gt;
&lt;p&gt;There is a nice tool which can help you if it is already too late to start from the beginning. It is called &lt;a href=&quot;https://unroll.me/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Unroll Me&lt;/a&gt;, it will go through all your emails and find the marketing emails. Then show them in a nice elegant list, which will allow you to unsubscribe from them in one click. I would recommend using this if you have no sensitive information in your Inbox because you are giving the company to parse all of your emails. There&apos;s no harm in being careful right?&lt;/p&gt;
&lt;h3&gt;Delete emails you don&apos;t need&lt;/h3&gt;
&lt;p&gt;If there is an email you clearly won&apos;t read and never will, then why not delete it rather than letting it pile up in your Inbox? Sure, mail clients such as Gmail has very good search functionality. But they will still slow you down more or less. If you are unsure about an email, definitely keep it. But for some useless marketing material sending it straight to the bin is not a bad idea.&lt;/p&gt;
&lt;h3&gt;Use Gmail&apos;s Inbox App&lt;/h3&gt;
&lt;p&gt;If organising email is not your thing, then Google&apos;s new (well, sort of) Inbox app on both mobile and the web app will make things really easy. It is going for the philosophy that no one needs to delete emails or manage them, the important thing is for their search to understand what you want and always return the emails you wish to see. You can pin important emails and set timers on emails reminders, but there is no need to do anything more. The app is meant to handle the rest for you.&lt;/p&gt;
&lt;h3&gt;Use Gmail&apos;s support for different alias&lt;/h3&gt;
&lt;p&gt;For easy Inbox management, use the infinite number of Email addresses Gmail gives you. This is a cool tip I learned recently, and you can read more about it &lt;a href=&quot;http://fieldguide.gizmodo.com/how-to-use-the-infinite-number-of-email-addresses-gmail-1609458192&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One trick you may or may not have picked up about Gmail is that you can add in periods anywhere in the front part of your address and it makes no difference whatsoever: john.smith@gmail.com works just the same as johnsmith@gmail.com. What&apos;s more, you can add a plus sign and any word before the @ sign (e.g. johnsmith+hello@gmail.com) and messages will still reach you. If these tweaks make no difference, then why use them? One major reason: filters.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So assume my Gmail address is name@gmail.com. Rather than using this email address to sign up to technology news website, I can actually sign up to newsletters using name.newsletter@gmail.com. With some simple filters, I can easily manage all of my subscriptions from here. It makes it easier to search and maintain later on as well.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Fixed Windows 10 cannot shutdown issue]]></title><description><![CDATA[<p>I normally bounce around between Linux, Mac OS and Windows for different tasks. I mainly use Windows for gaming or consuming media, so there isn't much software installed on my machine. Not too sure when it started, but I have been having this weird issue where Windows doesn't turn off my machine when I click on <code class="language-text">Shutdown</code>. It would just <code class="language-text">Log</code> me out rather than do a full shutdown. What's even weirder is that <code class="language-text">Restart</code> works just fine. I was doing a bit of searching around, I managed to resolve it. But I didn't find any of the instructions that was easy to follow. Hence why I'm creating this post.</p>]]></description><link>https://blog.hao.dev/fixed-windows-10-cannot-shutdown-issue</link><guid isPermaLink="false">https://blog.hao.dev/fixed-windows-10-cannot-shutdown-issue</guid><pubDate>Mon, 12 Dec 2016 18:23:00 GMT</pubDate><content:encoded>&lt;p&gt;I normally bounce around between Linux, Mac OS and Windows for different tasks. I mainly use Windows for gaming or consuming media, so there isn&apos;t much software installed on my machine. Not too sure when it started, but I have been having this weird issue where Windows doesn&apos;t turn off my machine when I click on &lt;code class=&quot;language-text&quot;&gt;Shutdown&lt;/code&gt;. It would just &lt;code class=&quot;language-text&quot;&gt;Log&lt;/code&gt; me out rather than do a full shutdown. What&apos;s even weirder is that &lt;code class=&quot;language-text&quot;&gt;Restart&lt;/code&gt; works just fine. I was doing a bit of searching around, I managed to resolve it. But I didn&apos;t find any of the instructions that was easy to follow. Hence why I&apos;m creating this post.&lt;/p&gt;&lt;h3&gt;Step 1&lt;/h3&gt;
&lt;p&gt;Go to &lt;code class=&quot;language-text&quot;&gt;Control Panel&lt;/code&gt; and search for &apos;power&apos;. As soon as you stop typing (wait a couple of seconds) then you should see the results coming back.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/5zDL2bGW1GqKOksewWaYyu/22efc4e601f25788c3684496410aaf5d/search-for-power.JPG&quot; alt=&quot;search-for-power&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Step 2&lt;/h3&gt;
&lt;p&gt;Click on &lt;code class=&quot;language-text&quot;&gt;Change what the power buttons do&lt;/code&gt;. It should take you to the follow screen.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2Cw6wPgSkoKAUEY4yOk8uy/c69d567a579d7f2cd5758a76e7882bd1/fast-start.JPG&quot; alt=&quot;fast-start&quot;&gt;&lt;/p&gt;
&lt;h3&gt;Step 3&lt;/h3&gt;
&lt;p&gt;Ensure the &lt;code class=&quot;language-text&quot;&gt;Turn on fast start-up (recommended)&lt;/code&gt; option is unchecked like shown in the picture above.&lt;/p&gt;
&lt;p&gt;For some reason, this functionality is causing problems for a lot of people. I guess this just proves that I am correct not choosing Windows for development.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Stopping ng-click from being fired in Angular (1.x) directive]]></title><description><![CDATA[<p>In the project I'm working on right now, we needed a way to disable some input fields based on a condition which is pretty common. I ended up writing a directive to reduce the amount of duplicated code and keeping it clean and simple. However, there was one problem, we didn't use the default checkboxes. Instead, we ended up using the icons from fonts-awesome library (<a href="http://fontawesome.io/icon/check-square-o/" target="_blank" rel="nofollow noopener noreferrer">Checked Icon</a>, <a href="http://fontawesome.io/icon/square-o/" target="_blank" rel="nofollow noopener noreferrer">Unchecked Icon</a>).</p>]]></description><link>https://blog.hao.dev/stopping-ng-click-from-being-fired-in-angular-1-x-directive</link><guid isPermaLink="false">https://blog.hao.dev/stopping-ng-click-from-being-fired-in-angular-1-x-directive</guid><pubDate>Fri, 18 Nov 2016 22:34:00 GMT</pubDate><content:encoded>&lt;p&gt;In the project I&apos;m working on right now, we needed a way to disable some input fields based on a condition which is pretty common. I ended up writing a directive to reduce the amount of duplicated code and keeping it clean and simple. However, there was one problem, we didn&apos;t use the default checkboxes. Instead, we ended up using the icons from fonts-awesome library (&lt;a href=&quot;http://fontawesome.io/icon/check-square-o/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Checked Icon&lt;/a&gt;, &lt;a href=&quot;http://fontawesome.io/icon/square-o/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Unchecked Icon&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;Here is an example of how we implemented the checkbox:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;i&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;aria-hidden&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;doStuff()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checked ? &lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;fa fa-check-square-o&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt; : &lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;fa fa-square-o&lt;span class=&quot;token punctuation&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;i&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;checkbox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token special-attr&quot;&gt;&lt;span class=&quot;token attr-name&quot;&gt;style&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token value css language-css&quot;&gt;&lt;span class=&quot;token property&quot;&gt;display&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; none&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-model&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;form.input1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Applying the directive on the input field doesn&apos;t really work since it is hidden. The right thing to do is to apply the directive on the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;i&gt;&lt;/code&gt; tag itself. However, disabling &lt;code class=&quot;language-text&quot;&gt;&amp;lt;i&gt;&lt;/code&gt; doesn&apos;t do much, it will still allow clicking and thus firing off the function within &lt;code class=&quot;language-text&quot;&gt;ng-click&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;The Solution&lt;/h3&gt;
&lt;p&gt;I spend hours looking for a simple solution that will do something along the lines of &lt;code class=&quot;language-text&quot;&gt;event.stopPropagation()&lt;/code&gt; when the checkbox icon is clicked. This should be enough to prevent &lt;code class=&quot;language-text&quot;&gt;ng-click&lt;/code&gt; being triggered.&lt;/p&gt;
&lt;p&gt;At the end, I managed to find the solution (see below).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;app&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;directive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;stopNgClick&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;restrict&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;A&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token literal-property property&quot;&gt;link&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token function-variable function&quot;&gt;pre&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;scope&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; element&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attrs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token function&quot;&gt;element&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;bind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;click touchstart&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;stopImmediatePropagation&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                    e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;preventDefault&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The problem I was facing was due to the directive is doing default postLink, and after changing it to preLink everything worked as expected.&lt;/p&gt;
&lt;h3&gt;Additional Reading&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;http://odetocode.com/blogs/scott/archive/2014/05/28/compile-pre-and-post-linking-in-angularjs.aspx&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Compile, Pre, and Post Linking in AngularJS&lt;/a&gt; is a good article that focuses on the execution order of Linking and Compile.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Use brain or password manager]]></title><description><![CDATA[<p>I struggled with this question for a long time. It is contradictory in a way that using a password manager should make everything more secure, but using a master password introduces a single point of failure. So is it more or less secure? Given the number of accounts I have created over the years, I would say going with a password manager is the better choice and stop repeating same passwords on different sites. This way hackers can only hack into one of your accounts if they hack into a site.</p>]]></description><link>https://blog.hao.dev/use-brain-or-password-manager</link><guid isPermaLink="false">https://blog.hao.dev/use-brain-or-password-manager</guid><pubDate>Mon, 31 Oct 2016 22:33:00 GMT</pubDate><content:encoded>&lt;p&gt;I struggled with this question for a long time. It is contradictory in a way that using a password manager should make everything more secure, but using a master password introduces a single point of failure. So is it more or less secure? Given the number of accounts I have created over the years, I would say going with a password manager is the better choice and stop repeating same passwords on different sites. This way hackers can only hack into one of your accounts if they hack into a site.&lt;/p&gt;&lt;p&gt;In terms of password managers, there are quite a few choices to choose from currently. I have tried few of them and want to share my experience here. I will start by listing some of the problems I have experienced, some personal tips, and finish off with a few recommendations.&lt;/p&gt;
&lt;h3&gt;Avoid password managers that store your data on their servers&lt;/h3&gt;
&lt;p&gt;The idea of having all my security details stored on a company&apos;s server makes me very uncomfortable. Not only can hackers crack open their server and have access to all of my details, it makes my insanely long master password essentially useless. Putting security aside for a minute, imagine you put all your passwords on a service like Lastpass. One day their server goes down, then you cannot even access your accounts. I think the best way is for the password manager act as a client, the storage of the passwords should not be their concern. I prefer saving the password database on cloud storage services such as Google Drive, One Drive and etc. This means I don&apos;t have to worry about people stealing my data from external servers. Sure, they could try to steal the password database, but since it is encrypted it will take them maybe 100 years to decrypt the content. Alternatively, they can steal my master password. However, without access to my cloud storage, the master password is useless on its own. To summarise, cloud storage beats service provider&apos;s servers.&lt;/p&gt;
&lt;h3&gt;Bad platform support&lt;/h3&gt;
&lt;p&gt;Do not choose a service that does not offer both mobile and desktop support. This was a tricky problem because I need it to work with Android, Mac, Windows and Linux. This was often the deal breaker, just think it through since migrating to another service later will be time-consuming and expensive.&lt;/p&gt;
&lt;h3&gt;Fingerprint support is a must for mobile Apps&lt;/h3&gt;
&lt;p&gt;I have tried a few password managers on Android and I was surprised how they did not include support for fingerprint access. Instead, they ask the user to enter an extremely secure master password. Can you imagine every time before you access your amazon account you have to spend about 10 seconds login into the password manager app and find your Amazon password? Compare this to fingerprint access which will take maybe half of a second? So another thing to look out for is fingerprint support on mobile.&lt;/p&gt;
&lt;h3&gt;Subscription VS one-time payment&lt;/h3&gt;
&lt;p&gt;Personally, I much prefer one-time payment services because it is generally cheaper in the long run. However, something to consider here is that subscription-based service may have better long term support due to the fact they will have more money to invest back into the product.&lt;/p&gt;
&lt;h3&gt;My recommendations&lt;/h3&gt;
&lt;p&gt;I have talked a lot about what to look for and things to avoid. Here are a few products I would recommend, they are intended for different user groups and hopefully at least one of them will be useful for you.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There are quite a few open source projects out there, there is no reason not considering them because they are secure and is free to use. The most popular one is &lt;a href=&quot;http://keepass.info&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;KeePass Password Safe&lt;/a&gt; by far, it supports both desktop and mobile. It also lets user choose between local password database or cloud storage, amazing! The only drawback is the user experience, compared to other services its desktop and mobile apps are not as well polished.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.safe-in-cloud.com/en/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;SafeInCloud&lt;/a&gt; is my current service. It is well polished, has browser support, and is available on most platforms. It can be purchased with a one-time payment. Since this is not a subscription based service, I am hoping the project doesn&apos;t get abandoned over time. There is no Linux support right now which might turn some people away.&lt;/li&gt;
&lt;li&gt;My last recommendation is &lt;a href=&quot;https://lastpass.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;LastPass&lt;/a&gt;. I have used it for more than a year, and it is all you can hope to get for a password manager. It has auto-fill on desktop browsers and mobile apps. It supports all platforms (even Linux since it is browser based). Desktop features are free to use, however, mobile apps require paid subscription. Even the paid subscription is very reasonable, right now it is $14.40 for a year. Lastpass is almost perfection, my biggest problem with it is how it stores all user details on their server. I personally have moved away from them for this exact reason. However, if you only want to use it for non-critical account info like random website login then it is definitely the go-to choice. Otherwise, I recommend looking at the two alternatives above.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[What I love and hate about JavaScript]]></title><description><![CDATA[<p>JavaScript is a dynamically typed language, and that gives it a lot of edge in my opinion. It is one of the most used programming language right now. So definitely worth learning even if people do not want to head towards this direction in their careers.</p>]]></description><link>https://blog.hao.dev/what-i-love-and-hate-about-javascript</link><guid isPermaLink="false">https://blog.hao.dev/what-i-love-and-hate-about-javascript</guid><pubDate>Sat, 09 Jul 2016 22:32:00 GMT</pubDate><content:encoded>&lt;p&gt;JavaScript is a dynamically typed language, and that gives it a lot of edge in my opinion. It is one of the most used programming language right now. So definitely worth learning even if people do not want to head towards this direction in their careers.&lt;/p&gt;&lt;p&gt;I really enjoyed coding in JavaScript when I first started working with it. Some languages such as Java can be dry and boring to learn, due to the strictness of its syntax. For a new learner this reduces work efficiency, and makes the coding experience less enjoyable. With JavaScript it is a different story, the language is flexible and allow programmers to code the logic of their applications without having to be constantly interrupted by errors messages.&lt;/p&gt;
&lt;p&gt;Using undeclared variable in JavaScript is allowed and everything will work just fine. Whereas other languages such as Java will throw about hundreds of errors (okay, I might have exaggerated) when compiled. Here&apos;s an example with undeclared variables.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
z &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this will give you 13&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is amazing, because when I am trying to code something there&apos;s no need to think about the type or memory allocation when compared to languages such as C. It might not seem like a big thing, but it takes an extra step from the process of converting your thoughts into code.&lt;/p&gt;
&lt;p&gt;Now, on to the problems. Even for experienced developer it is still a terrible idea to code like this. The lack of strictness makes it very hard for people to understand the  code. JavaScript does some weird interpretations of undeclared variables.&lt;/p&gt;
&lt;p&gt;If there is a bug, it will be very difficult to spot even for the person who wrote the code. It doesn&apos;t take too many badly managed code base to convince someone to write good code. Let&apos;s go through some examples.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Undeclared variable within a function becomes a global variable:&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// can use x here outside of the function&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Look at this &lt;a href=&quot;http://stackoverflow.com/questions/5113374/javascript-check-if-variable-exists-is-defined-initialized&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;stackoverflow &lt;/a&gt; question and see how confusing it is to just check if a variable has been set&lt;/li&gt;
&lt;li&gt;Types are not what you expect&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;             
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; y &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;===&lt;/span&gt; y&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// is false because x is a string and y is an object&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Type conversion when you don&apos;t expect it&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hello&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;// x is a string&lt;/span&gt;
x &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;               &lt;span class=&quot;token comment&quot;&gt;// now x is an integer&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If the above is not enough to convince you dynamic type is not enough there are plenty of other examples on the web. In general this makes large web applications extremely unstable and difficult to maintain. Through experience I think everyone will slowly feel the pain. I was such a strong supporter of dynamic languages, but looking at code bases with terrible code quality makes it really hard to appreciate the benefits it brings.&lt;/p&gt;
&lt;p&gt;Luckily, we can stop this by doing the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using &lt;code class=&quot;language-text&quot;&gt;&apos;use strict&apos;;&lt;/code&gt; within functions/scripts, this will enforce some strict nature in the code&lt;/li&gt;
&lt;li&gt;Follow a JavaScript Style Guide within your team to enforce the coding standard&lt;/li&gt;
&lt;li&gt;Using a JavaScript Linter with your IDE or Text-editor&lt;/li&gt;
&lt;li&gt;Add a linter check in the CI process as well to doubly ensure code quality&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Learning React and Flux - Introduction & Comparison with AngularJS 1.x]]></title><description><![CDATA[<p>Recently, the community of React has been exploding. AngularJS 2.0 has not convinced me that it is worth investing time in. So I have decided to start learning React with Flux Architecture. This blog is the summary of my recent learning of React and Flux. I am just writing about my thinking/learning process, so apologies if the structure is not very easy to understand.</p>]]></description><link>https://blog.hao.dev/learning-react-and-flux-introduction-and-comparison-with-angularjs-1-x</link><guid isPermaLink="false">https://blog.hao.dev/learning-react-and-flux-introduction-and-comparison-with-angularjs-1-x</guid><pubDate>Wed, 08 Jun 2016 22:27:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, the community of React has been exploding. AngularJS 2.0 has not convinced me that it is worth investing time in. So I have decided to start learning React with Flux Architecture. This blog is the summary of my recent learning of React and Flux. I am just writing about my thinking/learning process, so apologies if the structure is not very easy to understand.&lt;/p&gt;&lt;p&gt;I just want to clarify I am using AngularJS 1.x for comparison just to show some of the key differences I have learned with React, not to say AngularJS is a bad framework. I am also aware that AngularJS 2.0 is out and Angular team also fixed a lot of the problem mentioned in the blog. But I&apos;ll be using AngularJS 1.x for comparisons, because I am not familiar AngularJS 2.0.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;React is very different to Angular, both in terms of philosophy and architecture. The first thing to know is that, React tries to add HTML in JavaScript code and AngularJS tries to add JavaScript code to HTML.&lt;/p&gt;
&lt;p&gt;An example of Angular Code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-controller&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;my-ctrl&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;ng-class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;commentBox&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    Hello, world! I am a CommentBox.
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;An example of React Code:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; CommentBox &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; React&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;createClass&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token function-variable function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;div className&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;commentBox&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
        Hello&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; world&lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;I&lt;/span&gt; am a CommentBox&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;div&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
ReactDOM&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;CommentBox &lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  document&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;getElementById&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;content&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;So what else did I learn?&lt;/p&gt;
&lt;p&gt;when AngularJS was originally developed back in 2009, its two way binding shocked the web development world. It is miles more elegant and developer friendly compared to jQuery which dominated the JavaScript world before it. Two way binding is great (for small/single-page applications).&lt;/p&gt;
&lt;p&gt;AngularJS is designed with MVC architecture. HTML is the View and it is what the user sees (the DOM), JavaScript is the Controller (the business logic behind views), finally the Model is the data shown to the user in the view and that the user interacts with. The main benefit of MVC architecture is that, now it is also possible to separate out the HTML and JS code into different files (No longer will you see PHP, CSS, JS and HTML in the same file, massive win!).&lt;/p&gt;
&lt;p&gt;AngularJS&apos; problem arrises when you try to build large or complex applications, due to the inefficient way it renders the page. AngularJS tried to address the issue in later releases, and people have found plenty to &quot;tips&quot; and hacks to improve its performance. But the problem hasn&apos;t gone away, and as the application scale it will remain to be the blocking factor. At least, this is what I experienced with it. For me the reason to look towards React is the performance gain when compared to AngularJS.&lt;/p&gt;
&lt;p&gt;React is component based architecture, it wants you to forget the current structure of your page and re-imagine it in different component blocks. Menu header can be a block, since it is a sufficient element and is independent to other elements on the page. By separating its logic away from the rest of the page will make it easier to develop, test and maintain over time. React wants you to do this for all elements on the page. As a result this means every page is made up of these components and together they form the entire site. It also allows refactoring of repetitive code, again another big plus.&lt;/p&gt;
&lt;p&gt;This makes maintenance a lot easier in large applications. Imagine we have to update the look and feel of all the checkbox on the site, ideally we have already made checkbox into its own component. So it is as simple as going into the component file and making the changes we need, upon refresh we should see our changes on every single checkbox on this site. In AngularJS, we need to go to each page and update it. If you miss one, well, no one will know until someone reports it as a bug.&lt;/p&gt;
&lt;p&gt;If you are interested the differences between these two frameworks, there are plenty of comparisons so feel free to read up on it. But this is as much as I&apos;ll cover in this blog.&lt;/p&gt;
&lt;h1&gt;Why is React&apos;s performance better&lt;/h1&gt;
&lt;p&gt;To me the main differences is not the components implementation, for example you can use the React architecture in an AngularJS app if you wanted to. In fact, I have personally worked on this kind of projects before. I have to say it does help the problem a little. But this does not solve the performance issue we mentioned above. So how does React solve it?&lt;/p&gt;
&lt;p&gt;React is super clever when it comes to rendering. Like a person, it thinking about something first, decide what to do, then go and do it. What do I mean? Let&apos;s look as an example:&lt;/p&gt;
&lt;p&gt;With AngularJS, it tries to do exactly what it is told. If you have a list of 100 items, 1 is updated, then it tries to render all 100 items again. Sure there are ways to fix/optimise it, but not everyone will know how. With React however, it has this concept of Virtual Dom, here it does exactly what AngularJS is doing. But since it is virtual and exists only in memory, thus it is super fast to compute. Then React will use its &apos;diff&apos; algorithm to compare the virtual Dom and actual Dom, where ever it finds a difference it will update it and render the differences. In reality, this means a very small amount of re-rendering has been done on the actual Dom. Hence why React&apos;s performance is so much better.&lt;/p&gt;
&lt;h1&gt;Why Flux is the way forward for large applications&lt;/h1&gt;
&lt;p&gt;Angular&apos;s two way binding is great, it is easy very developer friendly. But as more and more functionalities  are added to the app, things will get a little hard to keep track of. In the diagram below, we are displaying the same information in 3 places. If one is changed, all needs to be updated. This is a very simple example, but imagine this 10 or even 100 times more complex. This problem is only worse with React, since developers need to keep track of all the components than just a single page.&lt;/p&gt;
&lt;p&gt;Facebook has certainly had a lot of issues in this area, this is why they created the Flux architecture. It restricts the web application to one way data flow (see diagram below).&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/ba1hGCXK36Suiqmk0aGO2/1f1c8d744daddc5e9e0309f807adf197/08192015-1.png&quot; alt=&quot;08192015-1&quot;&gt;&lt;/p&gt;
&lt;p&gt;Wait, isn&apos;t this taking a step back and going back to how jQuery does things? Well, not exactly, one way data flow is not the same as one way binding. The core concept is this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Every time user takes any action within the app, the action is recorded and then broadcasted to every component. It is up to each component to decide what to do with this action. So rather than calling 3 functions updating the value in 3 different places, everything will be updated automatically if they receive the relevant actions. This massively reduces complexity within large applications. Going forward, I definitely want to be using Flux in my React applications.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A final comment, I find React a little difficult to read compared to AngularJS. Maybe because I am just too used to writing Javascript code into HTML and not the other way around. Luckily, React created JSX to help developer to write code more efficiently, but in my opinion this makes readability of the code even worse.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Sources:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;React and Flux in the real world&quot; tutorial by Anthony P. Alicea.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[Hidden skills of a good developer]]></title><description><![CDATA[<p>Programming is a skill like any other, it is easy to get something done but difficult to do it well. Everyone can learn to code, there are so many resources teaching anyone to write their mobile app or website. So what defines a good developer from the crowed? Here's what I believe.</p>]]></description><link>https://blog.hao.dev/hidden-skills-of-a-good-developer</link><guid isPermaLink="false">https://blog.hao.dev/hidden-skills-of-a-good-developer</guid><pubDate>Sat, 09 Apr 2016 22:25:00 GMT</pubDate><content:encoded>&lt;p&gt;Programming is a skill like any other, it is easy to get something done but difficult to do it well. Everyone can learn to code, there are so many resources teaching anyone to write their mobile app or website. So what defines a good developer from the crowed? Here&apos;s what I believe.&lt;/p&gt;&lt;h3&gt;Design focused (UX)&lt;/h3&gt;
&lt;p&gt;In meeting, developers often expects people to understand the technical side of things. For example, it is so frustrating when the person you are talking to doesn&apos;t get what seems so elementary to you. I have been in similar situation, I get that. I think everyone should try to learn technical knowledge, in a scrum team that includes the Analysts and Product Owner.&lt;/p&gt;
&lt;p&gt;On the other hand, developer should not take anything granted either. Learn how to design a beautiful product, that has been thought from a UX point of view too. Sure technical side is important, but a good developer will suggest cool ways for the user to interact with the product since they know what is possible to achieve. I have been in teams where the art team will create all UI design and hand them over to the development team. To me, this sucks for the developers. Design is the soul of the product, it is where creativity shines. A good developer should be part of the design process, not copying design someone else made correct to the pixel.&lt;/p&gt;
&lt;h3&gt;Be responsible&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;Great powers comes with great responsibilities&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;Don&apos;t abuse your power as a developer.&lt;/strong&gt; No body likes reading crappy code and fixing bugs. If you are not sure, then check before committing your work to Github (or other Source Code Version Control tools).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&apos;t choose the easy way out.&lt;/strong&gt; People often write awful code due to time constraints. This leads to terrible and horrible code. Either rethink your logic and change the solution, or split up the logic into different methods/functions. So it is easier to understand and also makes it more reusable. Honestly, think about the poor guy picking up this code.&lt;/p&gt;
&lt;h3&gt;No limitation&lt;/h3&gt;
&lt;p&gt;Digital and Technology are changing all the time, try not focus on a single field. It is bad enough when developer separate themselves from designers, but splitting again between Mobile, Front-end web, Back-end web and Dev-ops? This goes back to work style, but for me I think work should be split into features so a developer will have to design the database schema, create relevant APIs, design the UI then link everything together.&lt;/p&gt;
&lt;h3&gt;Knowledgeable&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Spend time learning about a language.&lt;/strong&gt; Getting familiar with what is the best way to implement something in the language you are using. Which is the best loop to use? How to effectively manage memory? What are the constrains of the language? A good developer will know how to get most of the language, because you are using the language for a reason right? Otherwise, it probably makes more sense to use another language.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Understand possible solutions and how to choose the best one.&lt;/strong&gt; Developer often have 2 or more ways to implement something, they either uses different technology all together or just implementation logic within the code. Learn the different libraries, frameworks, tools or technologies. Know the difference between them, when is one of them is better than the rest, and the drawbacks as well.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Be the go to guy for your team.&lt;/strong&gt; If people always come up to you asking questions (how to do something) even those unrelated to your work, don&apos;t get annoyed instead be happy. That can only mean one thing, that you are a great developer and knows more than a lot of the people you work with. This is awesome, embrace it!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to implement correct REST API & RESTful Web Services?]]></title><description><![CDATA[<p>It is always easy to assume that you've understood something before someone showed you the correct solution. When I tried to follow someone's tutorial on how to create REST APIs using node.js and express.js, I found out I didn't get REST APIs completely right. I have also found out a few more things on the side, which I will also be sharing in this blog.</p>]]></description><link>https://blog.hao.dev/how-to-implement-correct-rest-api-and-restful-web-services</link><guid isPermaLink="false">https://blog.hao.dev/how-to-implement-correct-rest-api-and-restful-web-services</guid><pubDate>Tue, 22 Mar 2016 22:24:00 GMT</pubDate><content:encoded>&lt;p&gt;It is always easy to assume that you&apos;ve understood something before someone showed you the correct solution. When I tried to follow someone&apos;s tutorial on how to create REST APIs using node.js and express.js, I found out I didn&apos;t get REST APIs completely right. I have also found out a few more things on the side, which I will also be sharing in this blog.&lt;/p&gt;&lt;h2&gt;What is REST and RESTful?&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;While REST stands for Representational State Transfer, which is an architectural style for networked hypermedia applications, it is primarily used to build Web services that are lightweight, maintainable, and salable. A service based on REST is called a RESTful service.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So REST is a method for client side to communicating with the server side. The requests are structured in such a way that they often can be seen as black-box APIs, where anyone can make requests to the server with simple HTTP requests. This is how people can create their own clients for Twitter, Github and etc. Because all they need are the APIs released by companies like Twitter.&lt;/p&gt;
&lt;p&gt;A more traditional method is to use AJAX call to fetch XML formatted data from server. But XML is replaced by JSON in REST APIs, due to better readability and usability.&lt;/p&gt;
&lt;h2&gt;The different request methods&lt;/h2&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; are the most common ones used, but there are many others which are not fully and correctly utilized. I have see project using &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; when they should be using &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt;, or using &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt; when they should be using &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The HTTP verbs comprise a major portion of our “uniform interface” constraint and provide us the action counterpart to the noun-based resource. The primary or most-commonly-used HTTP verbs (or methods, as they are properly called) are &lt;code class=&quot;language-text&quot;&gt;POST&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;PUT&lt;/code&gt;, &lt;code class=&quot;language-text&quot;&gt;PATCH&lt;/code&gt;, and &lt;code class=&quot;language-text&quot;&gt;DELETE&lt;/code&gt;. These correspond to create, read, update, and delete (or CRUD) operations, respectively. There are a number of other verbs, too, but are utilized less frequently. Of those less-frequent methods, &lt;code class=&quot;language-text&quot;&gt;OPTIONS&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;HEAD&lt;/code&gt; are used more often than others.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I found the above explanation &lt;a href=&quot;http://www.restapitutorial.com/lessons/httpmethods.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;REST API Tutorial&lt;/a&gt; web site. Head over there for more detailed descriptions.&lt;/p&gt;
&lt;h2&gt;HTTP codes&lt;/h2&gt;
&lt;p&gt;The other thing I realized from the tutorial is that I really didn&apos;t know what I was doing with HTTP codes. There is a whole list which can be found on &lt;a href=&quot;https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;official W3 site&lt;/a&gt;. But following on from HTTP request methods, &lt;a href=&quot;http://www.restapitutorial.com/lessons/httpmethods.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;REST API Tutorial&lt;/a&gt; site also listed out the common HTTP codes as well.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; The request has completed successfully and results is returned.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;201&lt;/code&gt; The request has been fulfilled and resulted in a new resource being created.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;204&lt;/code&gt; The request has completed successfully, but nothing is returned.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; The server has not found anything matching the Request.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;409&lt;/code&gt; The request could not be completed due to a conflict with the current state of the resource. e.g. resource already exist.&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;500&lt;/code&gt; Internal server error, which can be used if the user is misusing the APIs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Consider to use these HTTP codes for the following requests&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;POST Method&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;201&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;409&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GET Method&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PUT Method&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;204&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;PATCH Method&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;204&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;DELETEMethod&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;200&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;404&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;Hypermedia API&lt;/h2&gt;
&lt;p&gt;This opened up a brand new door for me in terms of how API should be implemented for the sake of other developers. APIs can be messy, especially if it is not well documented. I have mentioned above that black-box API exists, where the developer using it does not need to know what is going on behind the scenes. &quot;But how do you know how the API works?&quot; you may ask. Well, hypermedia API is mention to help with that.&lt;/p&gt;
&lt;p&gt;The example below shows what was returned from a &lt;code class=&quot;language-text&quot;&gt;GET&lt;/code&gt; request. Many book results has been returned. Normally, the developer using this will need to go read the documentation in order to figure out how to query the book by ID. But in this case, within links attribute of the object, you can find a link. This is just to show a point, but in a production environment, this API should return a list of links, providing key insight into what you can actually do with the products returned. This will help people to understand the API without reading any additional docs, which means the usability is massively improved.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;json&quot;&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;_id&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;56f061f4364dd9282ead6e9e&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;My New Book&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;genre&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Fiction&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;author&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Hao&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;__v&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;read&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;links&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;self&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8000/api/books/56f061f4364dd9282ead6e9e&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  ...
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[The trend of 'clever' software]]></title><description><![CDATA[<p>There are plenty of companies researching into Artificial Intelligence (AI) and Machine Learning. Products such as Google Now, Siri and Cortana are all examples of combinations these. Apart from these big players, the trend is moving toward "vague" software usages, where the user specify something without being very exact. From these non-deterministic instructions, the software will be able to understand and execute and product the correct results. The following are just two examples I came across recently, both are pretty cool.</p>]]></description><link>https://blog.hao.dev/the-trend-of-clever-software</link><guid isPermaLink="false">https://blog.hao.dev/the-trend-of-clever-software</guid><pubDate>Mon, 21 Mar 2016 22:23:00 GMT</pubDate><content:encoded>&lt;p&gt;There are plenty of companies researching into Artificial Intelligence (AI) and Machine Learning. Products such as Google Now, Siri and Cortana are all examples of combinations these. Apart from these big players, the trend is moving toward &quot;vague&quot; software usages, where the user specify something without being very exact. From these non-deterministic instructions, the software will be able to understand and execute and product the correct results. The following are just two examples I came across recently, both are pretty cool.&lt;/p&gt;&lt;h3&gt;&lt;a href=&quot;https://numi.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;numi.io&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is no much I want to say about numi, it is a calculator for mac. Click on the image below to watch the video.&lt;/p&gt;
&lt;p&gt;[&lt;img src=&quot;//videos.ctfassets.net/zsjhz5fgria0/46jqGsVSO4q6wsycK24IOc/f560bbd9ba4a55830a77f468bcba460f/demo_2x.mp4&quot; alt=&quot;numi.io&quot;&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;http://www.datejs.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Datejs&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Datejs is a JavaScript date picker as suggested by its name. Its unique feature is that, rather than a date picker it allows user to type in plain english and based on that it can work out the date. It can understand inputs such as:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;today&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;tomorrow&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;July 2008&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;next friday&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;3 weeks from now&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;last April&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;2004.08.07&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;6/4/2005&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;8:15 PM&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;22:30:45&lt;/code&gt; &lt;code class=&quot;language-text&quot;&gt;+5years&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;I think a lot of this is hard coded, since Datejs still can&apos;t understand something like &lt;code class=&quot;language-text&quot;&gt;last day of march&lt;/code&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to create models in Mongoose]]></title><description><![CDATA[<p>Started working on a existing project built using MEAN.IO, and Mongoose framework was used as well. I found its documentation a little confusing, so here's what I found out after doing some research.</p>]]></description><link>https://blog.hao.dev/how-to-create-models-in-mongoose</link><guid isPermaLink="false">https://blog.hao.dev/how-to-create-models-in-mongoose</guid><pubDate>Fri, 18 Mar 2016 19:34:00 GMT</pubDate><content:encoded>&lt;p&gt;Started working on a existing project built using MEAN.IO, and Mongoose framework was used as well. I found its documentation a little confusing, so here&apos;s what I found out after doing some research.&lt;/p&gt;&lt;h3&gt;How to structure your model&lt;/h3&gt;
&lt;p&gt;Here&apos;s a very simple example of how to structure a model.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; mongoose &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;mongoose&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; Schema &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mongoose&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Schema&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; userSchema &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Schema&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
	  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	&lt;span class=&quot;token literal-property property&quot;&gt;team&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;required&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
	    &lt;span class=&quot;token literal-property property&quot;&gt;trim&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
	  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

userSchema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;methods&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;testFunction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

userSchema&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;statics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;defaultFunction&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	&lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

module&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; mongoose&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;User&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; userSchema&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;Difference between static and methods functions in Model&lt;/h3&gt;
&lt;p&gt;I got the following information from the &lt;a href=&quot;http://mongoosejs.com/docs/2.7.x/docs/methods-statics.html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Mongoose doc&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;To put it simply, Method lets you replace the original .find() function with custom find query (equivalent of SELECT in SQL).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;user
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;myOwnFind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;where&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;something&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;limit&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;exec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;err&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Statics are pretty much the same as methods but allow for defining functions that exist directly on your Model.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;userSchema.statics.search = function(){}&lt;/code&gt; would overwrite default &lt;code class=&quot;language-text&quot;&gt;user.search(){}&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;How to make calls your model from controller&lt;/h3&gt;
&lt;p&gt;Once the model is set up, it can then be called from controllers.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; User&lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;require&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&apos;../models/User&apos;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

Exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function-variable function&quot;&gt;create&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
	User&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;testFunc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;params&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;error&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
		&lt;span class=&quot;token comment&quot;&gt;// do something&lt;/span&gt;
	&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[How to make Zsh shell look nice on Mac Terminal]]></title><description><![CDATA[<p>I have already blogged about bash alternatives, Fish shell and Zsh in my previous blog <a href="//haodong.io/other-shell-alternatives-to-bash">here</a>. I have only started using Zsh recently due to recommendations from a colleague, so thought I'd give it a try. This blog is mainly focusing on how to make Zsh look nice, so no tutorial on installing it.</p>]]></description><link>https://blog.hao.dev/how-to-make-zsh-shell-look-nice-on-mac-terminal</link><guid isPermaLink="false">https://blog.hao.dev/how-to-make-zsh-shell-look-nice-on-mac-terminal</guid><pubDate>Wed, 16 Mar 2016 22:20:00 GMT</pubDate><content:encoded>&lt;p&gt;I have already blogged about bash alternatives, Fish shell and Zsh in my previous blog &lt;a href=&quot;//haodong.io/other-shell-alternatives-to-bash&quot;&gt;here&lt;/a&gt;. I have only started using Zsh recently due to recommendations from a colleague, so thought I&apos;d give it a try. This blog is mainly focusing on how to make Zsh look nice, so no tutorial on installing it.&lt;/p&gt;&lt;p&gt;With Zsh there are two main popular framework, &lt;a href=&quot;https://github.com/robbyrussell/oh-my-zsh&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;oh-my-zsh&lt;/a&gt; and &lt;a href=&quot;https://github.com/sorin-ionescu/prezto&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;prezto&lt;/a&gt;. From my research, it seems that oh-my-zsh is more complete in terms of resources and plugin, but prezto seems to have better performance. I went for performance myself, so prezto.&lt;/p&gt;
&lt;p&gt;There are already plenty of themes for prezoto:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;agnoster cloud damoekri giddie kylewest minimal nicoulaj paradox peepcode powerline pure skwp smiley sorin steeef adam1 adam2 bart bigfade clint elite2 elite fade fire off oliver pws redhat suse walters zefram&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;For some visuals refer to &lt;a href=&quot;http://mikebuss.com/2014/04/07/customizing-prezto/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;this&lt;/a&gt; blog where Mike Buss listed all the themes.&lt;/p&gt;
&lt;p&gt;I personally quite like the default theme &lt;code class=&quot;language-text&quot;&gt;sorin&lt;/code&gt;, so I kept it like that. But the colours looked really bad on all the different Terminal profiles on Mac. So I went ahead and add my own colours for it. I ended up using material design colours for this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/50M3g5ZUFigOMW8aOI8OiG/812a9c4bdab7bdbfcaab27f1cf5e3a52/Screen_Shot_2018-08-20_at_11.49.17_am.png&quot; alt=&quot;Screen Shot 2018-08-20 at 11.49.17 am&quot;&gt;
(screenshot updated on 20/08/2018, old link was broken)&lt;/p&gt;
&lt;p&gt;You can download the Terminal profile &lt;a href=&quot;https://gist.github.com/csukcc/79cf8e986f99b639aef3&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why testing isn't enough]]></title><description><![CDATA[<p>A lot of people talks about the benefits of Test-Driven Development, how great it is when you compare it with non-test driven development. People would give a massive list of advantages of using TDD. For me these are the core reasons:</p>]]></description><link>https://blog.hao.dev/why-testing-isn-t-enough</link><guid isPermaLink="false">https://blog.hao.dev/why-testing-isn-t-enough</guid><pubDate>Thu, 10 Mar 2016 22:18:00 GMT</pubDate><content:encoded>&lt;p&gt;A lot of people talks about the benefits of Test-Driven Development, how great it is when you compare it with non-test driven development. People would give a massive list of advantages of using TDD. For me these are the core reasons:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Writing tests first require you to really consider what you want from the code&lt;/li&gt;
&lt;li&gt;Makes writing unit test easy&lt;/li&gt;
&lt;li&gt;Act as documentation for the code&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is so much praise around it, to the point where people would believe it solves problem it is not meant to solve. For example, just because you use TDD does not mean your code has no bug. In fact, depending on developer it might not make any difference at all. If a good developer is writing the code, who has very clear head and knows exactly what he is doing. On top of that it takes a lot of brain power to think about all the different scenarios and design the possible inputs/outputs before hand. So it is kind of a moo point if it doesn&apos;t add too much value.&lt;/p&gt;
&lt;p&gt;The reason I wanted to write about this today is because I read a blog from Github introducing their Scientist 1.0 product. They highlighted a very good point, that &quot;tests aren&apos;t enough&quot;. The scenario they explained is when you have a legacy system, but wish to replace a component or part of the system to improve performance or whatever the reason is. Then how can you be confident that the new changes won&apos;t cause issues and trip over on production? The solution they gave was using this idea that if you want to replace a function in the system, then run the old and new function are the same time side-by-side for a period of time and compare their results. Record any difference in results and then remove the old function when you have enough confidence in the new function.&lt;/p&gt;
&lt;p&gt;Here&apos;s some sudo code to explain the concept:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;expriment&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  old &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;old &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    record data
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;old&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; x
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;do&lt;/span&gt; x
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Here&apos;s the original text from the article. Find &lt;a href=&quot;//githubengineering.com/scientist/&quot;&gt;Original Article&lt;/a&gt; here.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Why tests aren&apos;t enough&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to test correctness, you just write some tests for your new system, right? Well, not quite. Tests are a good place to start verifying the correctness of a new system as you write it, but they aren&apos;t enough. For sufficiently complicated systems, it is unlikely you will be able to cover all possible cases in your test suite. If you do, it will be a large, slow test suite that slows down development considerably.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;There&apos;s also a more concerning reason not to rely solely on tests to verify correctness: Since software has bugs, given enough time and volume, your data will have bugs, too. Data quality is the measure of how buggy your data is. Data quality problems may cause your system to behave in unexpected ways that are not tested or explicitly part of the specifications. Your users will encounter this bad data, and whatever behavior they see will be what they come to rely on and consider correct. If you don&apos;t know how your system works when it encounters this sort of bad data, it&apos;s unlikely that you will design and test the new system to behave in the way that matches the legacy behavior. So, while test coverage of a rewritten system is hugely important, how the system behaves with production data as the input is the only true test of its correctness compared to the legacy system&apos;s behavior.&lt;/p&gt;
&lt;/blockquote&gt;</content:encoded></item><item><title><![CDATA[Mobile and hybrid app testing tool - appium.io]]></title><description><![CDATA[<p>Came across this free app testing tool today, currently not much use for me as I mainly work on web related projects. But hopefully will be working on mobile projects in the future and this will be very useful. Just want to mention that this is a automation test framework from what I have seen so far, so not meant to be used to do manual testing.</p>]]></description><link>https://blog.hao.dev/mobile-and-hybrid-app-testing-tool-appium-io</link><guid isPermaLink="false">https://blog.hao.dev/mobile-and-hybrid-app-testing-tool-appium-io</guid><pubDate>Wed, 09 Mar 2016 21:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Came across this free app testing tool today, currently not much use for me as I mainly work on web related projects. But hopefully will be working on mobile projects in the future and this will be very useful. Just want to mention that this is a automation test framework from what I have seen so far, so not meant to be used to do manual testing.&lt;/p&gt;&lt;p&gt;The official site is &lt;a href=&quot;http://appium.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;appium.io&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[How to use tabs in Vue-strap]]></title><description><![CDATA[<p>Vue-strap is a repository that contains a set of native Vue.js components based on Bootstrap's markup and CSS. This is all very well and good, however, the second I tried to use this I came across issues.</p>]]></description><link>https://blog.hao.dev/how-to-use-tabs-in-vue-strap</link><guid isPermaLink="false">https://blog.hao.dev/how-to-use-tabs-in-vue-strap</guid><pubDate>Tue, 08 Mar 2016 21:59:00 GMT</pubDate><content:encoded>&lt;p&gt;Vue-strap is a repository that contains a set of native Vue.js components based on Bootstrap&apos;s markup and CSS. This is all very well and good, however, the second I tried to use this I came across issues.&lt;/p&gt;&lt;h3&gt;1. I didn&apos;t know how to import the components&lt;/h3&gt;
&lt;p&gt;I was using Webpack, and this is the way to import a component. The other notation did not work for me, probably for good reasons but I never found out why.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; alert &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vue-strap/src/alert&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;2. Requires Bootstrap CSS on the HTML page&lt;/h3&gt;
&lt;p&gt;This sounds stupid now I managed to work it out, but Vue CLI by default does not load CSS. So the simplest way is to include this in the &lt;code class=&quot;language-text&quot;&gt;&amp;lt;head&gt;&lt;/code&gt; tag of your HTML page.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;link&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;rel&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;stylesheet&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;href&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h3&gt;3. Tabs simply refuses to work&lt;/h3&gt;
&lt;p&gt;It took me a while to finally work this out, the documentation is simply rubbish when it came to explaining how Tabs works. The example code (listed below) looks pretty simple.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;html&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tabs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;one&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;two&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;tab&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;header&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;three&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;disabled&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    ...
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tab&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;tabs&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; tab &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vue-strap/src/tab&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I import the tab component surely this should work, right?&lt;/p&gt;
&lt;p&gt;No! This is only half of the story. Import tab is correct, but you know &lt;code class=&quot;language-text&quot;&gt;&amp;lt;tabs&gt;&lt;/code&gt;? Yep, that needs to be imported as well. The logical thing to call it would be &apos;Tabs.vue&apos;, but that would be way too easy. It is actually called &apos;Tabset.vue&apos;, so you got to also import the following to get tabs working.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; tabs &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;vue-strap/src/tabset&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Not sure why they thought it is a good idea to leave out this pretty important information, especially for someone who is new to Vue. But here you go, hopefully, saved you some time and pain.&lt;/p&gt;
&lt;p&gt;Joking aside, this is actually a pretty cool project, you can find it at &lt;a href=&quot;https://yuche.github.io&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;yuche.github.io&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Regex for getting the host of a website]]></title><description><![CDATA[<p>I ran into this problem where we wanted to strip away the url of different sites to its root url. On top of that I also wanted to remove anything before the domain name, for example for staging.domain.com I only want domain.com from it.</p>]]></description><link>https://blog.hao.dev/regex-for-getting-the-host-of-a-website</link><guid isPermaLink="false">https://blog.hao.dev/regex-for-getting-the-host-of-a-website</guid><pubDate>Wed, 17 Feb 2016 21:56:00 GMT</pubDate><content:encoded>&lt;p&gt;I ran into this problem where we wanted to strip away the url of different sites to its root url. On top of that I also wanted to remove anything before the domain name, for example for staging.domain.com I only want domain.com from it.&lt;/p&gt;&lt;p&gt;I ended up solving this issue using regular expression:&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;/[^.]*\.([^.]{2,}|[^.]{2,3}\.[^.]{2})$/&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Explaining from the last character of the regex to the first one:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;$&lt;/code&gt; is the end of string e.g. &apos;www.domain.co.uk&apos;&amp;#x3C;-(here)&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Within the () bracket there are two different matches e.g. (a|b) matches to a or b&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[^.]{2,}&lt;/code&gt;: matches to anything like ‘com’, ‘uk’, ‘london’ and etc&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[^.]{2,3}\.[^.]{2}&lt;/code&gt;: matches to anything like ‘co.uk’ or ‘com.us’ and etc&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code class=&quot;language-text&quot;&gt;[^.]*\.&lt;/code&gt; this matches against ‘domain.’ or ‘google.’&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some test domain urls are listed below, you can try it out on &lt;a href=&quot;https://regex101.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;regex101.com&lt;/a&gt; (make sure you click on JavaScript on the right hand side instead of pcre):&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;www.domain.com
www.domain.co.uk
www.domain.us
www.url2s.com
www.domain.london
www.url2s.travel
www.bbc.com
andigital.com
bbc.co.uk
www.domain.com.us
staging.su.ik.il.domain.org.uk
www.domain.xn----china&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item><item><title><![CDATA[Other shell alternatives to Bash]]></title><description><![CDATA[<p>Came across something called Fish Shell today, and after Google search took me to this page on unix-shells I realised how many different variation there are I can choose from. Each having their benefits, so far it seems ZSH has very good customisability and Fish Shell has very good out of the box experience with no configurations.</p>]]></description><link>https://blog.hao.dev/other-shell-alternatives-to-bash</link><guid isPermaLink="false">https://blog.hao.dev/other-shell-alternatives-to-bash</guid><pubDate>Mon, 18 Jan 2016 20:46:00 GMT</pubDate><content:encoded>&lt;p&gt;Came across something called Fish Shell today, and after Google search took me to this page on unix-shells I realised how many different variation there are I can choose from. Each having their benefits, so far it seems ZSH has very good customisability and Fish Shell has very good out of the box experience with no configurations.&lt;/p&gt;&lt;p&gt;I personally don’t feel confident enough to switch to something that is not Bash, just because how little I use it. But this could be very handy for someone who knows Bash like back of his hand and wants a bit more horse power.&lt;/p&gt;
&lt;p&gt;I ended up switching to ZSH a few weeks after this original post, and after a year I thought it would be a good chance to give an update. The setting up process is not as straight forward as I would like, but after everything has been set up my god it is amazing! It makes the whole terminal comes to life, awesome predictions and etc. I have even spent the time and added Material colour pallets to it. Here&apos;s a screenshot of my current set up.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6nOBkTboQwu4mekQWcwwQ0/a929b9cac81bd7d8671613039d167561/zsh.png&quot; alt=&quot;zsh&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[A useful tool when working with Regular Expression]]></title><description><![CDATA[<p>I was working with replacing strings using Regular Expression today and I found this amazing tools which saved me a lot of trouble. It allow custom text string to perform the regular expression on, and you can toggle between 3 different regular expression syntaxes: PHP, JavaScript and Python. There are also useful tips on the right hand side as well as a regex syntax look up table. Overall, a very handy free tool.</p>]]></description><link>https://blog.hao.dev/a-useful-tool-when-working-with-regular-expression</link><guid isPermaLink="false">https://blog.hao.dev/a-useful-tool-when-working-with-regular-expression</guid><pubDate>Sun, 03 Jan 2016 21:38:00 GMT</pubDate><content:encoded>&lt;p&gt;I was working with replacing strings using Regular Expression today and I found this amazing tools which saved me a lot of trouble. It allow custom text string to perform the regular expression on, and you can toggle between 3 different regular expression syntaxes: PHP, JavaScript and Python. There are also useful tips on the right hand side as well as a regex syntax look up table. Overall, a very handy free tool.&lt;/p&gt;&lt;p&gt;You can find it at &lt;a href=&quot;https://regex101.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;regex101.com&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Convert PDF to tables]]></title><description><![CDATA[<p>I recently needed to convert PDF documents which contains tables. From what I could find, there are only two good solutions out there.</p>]]></description><link>https://blog.hao.dev/convert-pdf-to-tables</link><guid isPermaLink="false">https://blog.hao.dev/convert-pdf-to-tables</guid><pubDate>Sun, 03 Jan 2016 20:41:00 GMT</pubDate><content:encoded>&lt;p&gt;I recently needed to convert PDF documents which contains tables. From what I could find, there are only two good solutions out there.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://pdftables.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;https://pdftables.com&lt;/a&gt; - A web app that could convert a PDF document to CSV file. Simply upload the file and wait for it do its magic. However, this is no longer free for everyone to use. After the free trial ends, a fee is needed to keep using its service.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;http://tabula.technology&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://tabula.technology&lt;/a&gt; - Tabula is a software which are available on Windows and Mac, it opens as a web application on your browser. The automatic mode is not as accurate as pdftables.com web app, but using manual selection.&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[How to uninstall from Debian Linux]]></title><description><![CDATA[<p>Linux is still something that's new to me, I know the basics like to install some software I need to run <code class="language-text">sudo apt-get update</code> and <code class="language-text">sudo apt-get install</code> in the terminal. But I never really knew how to 'undo' these actions, today when I was trying to fix another bug, I came across the following ways to uninstall the software are installed in Linux terminal.</p>]]></description><link>https://blog.hao.dev/how-to-uninstall-from-debian-linux</link><guid isPermaLink="false">https://blog.hao.dev/how-to-uninstall-from-debian-linux</guid><pubDate>Tue, 08 Dec 2015 20:35:00 GMT</pubDate><content:encoded>&lt;p&gt;Linux is still something that&apos;s new to me, I know the basics like to install some software I need to run &lt;code class=&quot;language-text&quot;&gt;sudo apt-get update&lt;/code&gt; and &lt;code class=&quot;language-text&quot;&gt;sudo apt-get install&lt;/code&gt; in the terminal. But I never really knew how to &apos;undo&apos; these actions, today when I was trying to fix another bug, I came across the following ways to uninstall the software are installed in Linux terminal.&lt;/p&gt;&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt-get remove&lt;/code&gt; - Packages installed are removed (Does NOT include configuration files).&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;sudo apt-get purge&lt;/code&gt; - Purge is identical to remove except that packages are removed and purged. Purge meaning that any configuration files are deleted too.&lt;/p&gt;
&lt;p&gt;This is going to so useful!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Moltin, a great service to consider for creating E-Commerce]]></title><description><![CDATA[<p>I have not personally used <a href="https://www.moltin.com/" target="_blank" rel="nofollow noopener noreferrer">Moltin</a>, however, based on my intraction with it so far it looks like a good service to consider when building an E-commerce platform. It is designed to take stress away from creating E-Commerce sites by letting the programmer focus on the creative and fun parts.</p>]]></description><link>https://blog.hao.dev/moltin-a-great-service-to-consider-for-creating-e-commerce</link><guid isPermaLink="false">https://blog.hao.dev/moltin-a-great-service-to-consider-for-creating-e-commerce</guid><pubDate>Mon, 07 Dec 2015 20:28:00 GMT</pubDate><content:encoded>&lt;p&gt;I have not personally used &lt;a href=&quot;https://www.moltin.com/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Moltin&lt;/a&gt;, however, based on my intraction with it so far it looks like a good service to consider when building an E-commerce platform. It is designed to take stress away from creating E-Commerce sites by letting the programmer focus on the creative and fun parts.&lt;/p&gt;&lt;p&gt;So what service do they actually provide? Well they allow you to configure everything, e.g. add categories to databases via UI on their site. Then later you can access it as an API call, so any data stored can be retrieved using a provided API. Database management and API creation is no longer your concern. Neither is the security and compliances for that matter, free account has 30, 000 API per month which is more than enough to get started with.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Creates beautiful timelines with Labella.js]]></title><description><![CDATA[<p>I came across this library today, and from a brief look it is both elegant and beautiful. I have seen a lot of timeline implementation before, but nothing as simple and nicely designed as this one. It is created and supported by Twitter, based on my experience with Bootstrap CSS framework by Twitter I think this will be an excellent framework as well.</p>]]></description><link>https://blog.hao.dev/creates-beautiful-timelines-with-labella-js</link><guid isPermaLink="false">https://blog.hao.dev/creates-beautiful-timelines-with-labella-js</guid><pubDate>Thu, 03 Dec 2015 20:12:00 GMT</pubDate><content:encoded>&lt;p&gt;I came across this library today, and from a brief look it is both elegant and beautiful. I have seen a lot of timeline implementation before, but nothing as simple and nicely designed as this one. It is created and supported by Twitter, based on my experience with Bootstrap CSS framework by Twitter I think this will be an excellent framework as well.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/W3Mbir7GCIuemeSKwM4Gm/34049ffc49c1c4f9f78a87d4816b04c4/Screenshot_2019-01-07_at_01.02.20.png&quot; alt=&quot;labella.js&quot;&gt;&lt;/p&gt;
&lt;p&gt;This is not something I personally need right now, but I&apos;d be glad to have in the back of my pocket. Click &lt;a href=&quot;http://twitter.github.io/labella.js/&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;here&lt;/a&gt; to go to Labella&apos;s official site.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Time to drop CSS and learn SASS/SCSS]]></title><description><![CDATA[<p>SASS/SCSS is the compiled version of CSS, it allows CSS to be written in a more dynamic and reusable way. The philosophy is make CSS, a very static language, into something that is more similar to a scripting language, then the SCSS file is then compiled back into a normal CSS file to be deployed. y introducing variables and splitting the single CSS file into different SASS/SCSS files (will be combined into one when releasing), this makes the CSS much easier to read and more maintainable. This is especially true when more than one developer is working on the same code base.</p>]]></description><link>https://blog.hao.dev/time-to-drop-css-and-learn-sass-scss</link><guid isPermaLink="false">https://blog.hao.dev/time-to-drop-css-and-learn-sass-scss</guid><pubDate>Mon, 30 Nov 2015 20:02:00 GMT</pubDate><content:encoded>&lt;p&gt;SASS/SCSS is the compiled version of CSS, it allows CSS to be written in a more dynamic and reusable way. The philosophy is make CSS, a very static language, into something that is more similar to a scripting language, then the SCSS file is then compiled back into a normal CSS file to be deployed. y introducing variables and splitting the single CSS file into different SASS/SCSS files (will be combined into one when releasing), this makes the CSS much easier to read and more maintainable. This is especially true when more than one developer is working on the same code base.&lt;/p&gt;&lt;p&gt;I personally prefer SCSS over SASS, to be honest there&apos;s not much difference between the two. I like SCSS because of it uses brackets to show the scope instead of just indentation. Curly brackets makes nested code much easier to read, this is very important since that is how to write CSS or SASS/SCSS.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://sass-lang.com/guide&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Here&lt;/a&gt; is the official site, which will explain everything in more depth with examples.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Awesome packages for atom]]></title><description><![CDATA[<p>I have been using Atom for more than a year, since it was in beta. There are many things I love about it, the most important thing is customizability. It allows anyone to create an Text Editor that is designed from the ground up for themselves. In this blog, I will be listing some of my favourite packages.</p>
<p>Update: Tweaked content on 30th Jan 2017
Update: Fixed some broken image links on 7th Jan 2019</p>]]></description><link>https://blog.hao.dev/awesome-packages-for-atom</link><guid isPermaLink="false">https://blog.hao.dev/awesome-packages-for-atom</guid><pubDate>Mon, 23 Nov 2015 21:23:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been using Atom for more than a year, since it was in beta. There are many things I love about it, the most important thing is customizability. It allows anyone to create an Text Editor that is designed from the ground up for themselves. In this blog, I will be listing some of my favourite packages.&lt;/p&gt;
&lt;p&gt;Update: Tweaked content on 30th Jan 2017
Update: Fixed some broken image links on 7th Jan 2019&lt;/p&gt;&lt;h2&gt;Must Haves&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/atom-beautify&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Atom Beautify&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most annoying thing about reading other people&apos;s code is that they always seems to be using a different indentation and formatting to you. With this package, you can convert almost any code (supports stupid amount of programming languages and frameworks) to a clean and nice to read format. If there is something you don&apos;t like about the default settings then there&apos;s option to customise each language and how it converts them.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3puck6TqAo6k4eIOQwA8ew/0755ca86de2bdf049cb6ecf986226ba7/68747470733a2f2f636c6f75642e67697468756275736572636f6e74656e742e636f6d2f6173736574732f313838353333332f31363534323732372f64623532&quot; alt=&quot;Before&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/3C6uLFcwiIIIyW2awcY2Om/e11659cc1ffe3dacc424b9d5c5222bbb/68747470733a2f2f636c6f75642e67697468756275736572636f6e74656e742e636f6d2f6173736574732f313838353333332f31363534323732382f64636163&quot; alt=&quot;After&quot;&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/file-icons&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Files Icon&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There is no reason not to install this package, no matter what you use Atom for this will make it easier for you by changing the default file icon to these nice looking icons. This will make files standout from each other, and in general improves the look and feel of the whole editor. Trust me when I say once you start using this, there is no going back. I mean take a look at these nice icons :D&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/50KOZM6bDGS4Wag6GI4owi/48d3363f720930974e2a1eae3be06b15/68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f66696c652d69636f6e732f61746f6d2f3637313437303666323638653235&quot; alt=&quot;File Icons&quot;&gt;&lt;/p&gt;
&lt;p&gt;For more advances users, there are also setting to resize, change colours and etc.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/project-manager&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Project Manager&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have quite a few projects going on at one point, switching between them is really annoying. Now with this package, you can switch between any project with a short cut.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/58sr7kGrfOwoEWiE2AWouc/04c8e937a8952410d35aa1d8993c3753/68747470733a2f2f7261772e6769746875622e636f6d2f64616e69656c62726f64696e2f61746f6d2d70726f6a6563742d6d616e616765722f6d61737465722f&quot; alt=&quot;Package Manager&quot;&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/sync-settings&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Sync Settings&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Switching between computers means setting up everything, and it is a pain to re-configure IDE/Text Editors. With this package you will be able to backup everything on Github Gist.&lt;/p&gt;
&lt;p&gt;It could sync almost everything:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sync Atom&apos;s and package settings&lt;/li&gt;
&lt;li&gt;Sync installed packages&lt;/li&gt;
&lt;li&gt;Sync user keymaps&lt;/li&gt;
&lt;li&gt;Sync user styles&lt;/li&gt;
&lt;li&gt;Sync user init script&lt;/li&gt;
&lt;li&gt;Sync snippets&lt;/li&gt;
&lt;li&gt;Sync user defined text files&lt;/li&gt;
&lt;li&gt;Manual backup/restore to a gist&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/pigments&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Pigments&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Pigments will scan source files in your project directories looking for colors and will build a palette with all of them. Then for each opened file, it will use the palette to evaluate the value of a given color. This works with CSS or even SASS/SCSS, and makes it a lot easier to read other people&apos;s code as well.&lt;/p&gt;
&lt;p&gt;This package has a lot of settings, the image below shows all the different visual ways to show the colour indicator. I personally prefer the coloured dot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1O6yludccsoquqwQMCKEE6/dbebfebf07dd7ee8eefdaf9d642efdca/687474703a2f2f61626533332e6769746875622e696f2f61746f6d2d7069676d656e74732f7069676d656e74732e6769663f7261773d74727565&quot; alt=&quot;Different Pigments setup&quot;&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/minimap&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Minimap&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is a package ported over from Sublime Text I believe. It replaces the scroll bar of the opened document with a shrinked version, so when you won&apos;t ever need to scroll blindly anymore. If you are familiar with the page layout, it might even be possible to go to the correct section by simply clicking on it.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/1hHq4MQ2UUuAIiwKmIIeu0/f9c60091d80c7c9b063cb8af320755d7/68747470733a2f2f6769746875622e636f6d2f61746f6d2d6d696e696d61702f6d696e696d61702f626c6f622f6d61737465722f7265736f75726365732f7363&quot; alt=&quot;Minimap&quot;&gt;&lt;/p&gt;
&lt;p&gt;If you do decide to use Minimap, here&apos;s a few other packages to be used with this one &lt;a href=&quot;https://atom.io/packages/minimap-highlight-selected&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;minimap-highlight-selected&lt;/a&gt; and &lt;a href=&quot;https://atom.io/packages/minimap-pigments&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;minimap-pigments&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Team Tools&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/linter&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Linter&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Code linting is amazing, it forces consistency and ensures the whole development team is following the same format. I don&apos;t know how many versions of indentation I saw in previous projects before, the answer is too damn many. With linter, you won&apos;t be the &quot;guy&quot; that breaks the style everyone agreed on. In my previous work place, we went as far as making passing linter a process in the build process. So for each of the wrong styling added, it will throw and error/warning.&lt;/p&gt;
&lt;p&gt;Obviously, this is the base package for language specific linters e.g. eslint for Javascript.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/linter-write-good&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Linter Write Good&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you have heard of or used Grammarly, this is basically a simpler version of that in an Atom package. It will give writing suggestions and makes sure you write in proper English. I don&apos;t think the suggestions are as good, but it can help to identify some simple grammatical errors.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/editorconfig&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Editor Config&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Following on from the Linter package, this is also designed to keep the code conssitent in a dev team. Unlike the linter, this will not throw any errors/warnings. Instead it will update the current editor&apos;s config to whatever configuration is set. Let&apos;s the use the indentation example again, if someone sets the indentation to 4 spaces in the config I will automatically get that setting when I load the project.&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/highlight-selected&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Highlight Selected&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Double click on a word to highlight it throughout the open file. There&apos;s not much else to say really!&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/BgQsW9Vf8ccYekeokK8s6/5e3ef40877e09218b3f00edc6c460dbc/687474703a2f2f692e696d6775722e636f6d2f4335466e7a7a512e676966&quot; alt=&quot;Highlight Selected&quot;&gt;&lt;/p&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/indent-guide-improved&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Indent Guide Improved&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The default indentation on Atom is not very good, this package will show the indentation line depending when you click. See the Gif below to see it in action.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/4FqBr3VJgA0wkuQWeuyQS6/77c56b2d7c5956d04d55f377af764e9c/68747470733a2f2f7261772e67697468756275736572636f6e74656e742e636f6d2f68617261692f696e64656e742d67756964652d696d70726f7665642f6d61&quot; alt=&quot;Indent Guide Improved&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Web Development focused&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/autoclose-html&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Auto Close HTML&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Note: Used for web development&lt;/p&gt;
&lt;p&gt;In a lot of IDEs and TextEditors this would be a built-in feature, but since Atom is designed to be modular. The community has taken up the responsibility to add in these kind of useful features. This package will automatically add closing tags when you complete the opening tag. Sometimes it can be annoying in some scenario and I wish it can be a bit cleverer. But compared to the default behaviour, this is fantastic and makes writing HTML way easier.&lt;/p&gt;
&lt;h2&gt;Random And Fun&lt;/h2&gt;
&lt;h3&gt;&lt;a href=&quot;https://atom.io/packages/atom-clock&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Atom Clock&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Sometimes I want to cut all distractions out, and maximizes the Atom window to prevent myself from seeing notifications and etc. Hiding everything can be annoying when I want to check the time. With this package you will see a little clock on the bottom status bar, neat right? This package also allowing many variations of date/time formats and also available in different languages.&lt;/p&gt;
&lt;h3&gt;What Else? Well, Just Be Creative&lt;/h3&gt;
&lt;p&gt;The best functionality of Atom is not any of its packages, the ability to combine x number of packages to achieve one desired purpose. Here is a quick example of how I used different packages together to create a new feature.&lt;/p&gt;
&lt;p&gt;When I&apos;m working on web development projects, I often finds it annoying how I have to manually navigate to a HTML and double clicking on it to open it in a browser. I wanted an easy to do this when playing around with HTML, CSS and JS on my machine. The first package I installed is &lt;a href=&quot;https://atom.io/packages/tool-bar&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Tool Bar&lt;/a&gt;, it creates a empty toolbar in Atom. To populate the empty toolbar, I installed another package &lt;a href=&quot;https://atom.io/packages/flex-tool-bar&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Flex Tool Bar&lt;/a&gt;. This adds the necessary buttons. The final package I needed was &lt;a href=&quot;https://atom.io/packages/open-in-browser&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Open In Browser&lt;/a&gt;, which does what the name suggests. I to add a button to the Flex Tool Bar, I added the following &lt;code class=&quot;language-text&quot;&gt;cson&lt;/code&gt; into the config file.&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;javascript&quot;&gt;&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;button&quot;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;icon&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;chrome&apos;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;open-in-browser:open&apos;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;tooltip&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;Open in Chrome&apos;&lt;/span&gt;
  &lt;span class=&quot;token literal-property property&quot;&gt;iconset&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&apos;icomoon&apos;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some may think this is stupid, why don&apos;t they just create a package that does by default? Well, by splitting up the modular package like this, it makes it a lot easier to build on top of other people&apos;s work rather than keep reinventing the wheel.&lt;/p&gt;
&lt;h2&gt;Final Thoughts&lt;/h2&gt;
&lt;p&gt;I personally have way more packages installed. Since a lot of it is focused on the work I am working on right now and might not apply to a lot of people, so I have decided to leave them out of this post. But there is a page on Atom&apos;s website which lists the popular packages and themes, it is worth checking it out every so often and see if there&apos;s anything new that&apos;s been added. Finally, I hope this has been useful and leave me any package suggestions in the comments below!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Mouse software for gaming on Mac]]></title><description><![CDATA[<p>When using normal Windows mouse on a Mac, there were some weird acceleration applied to the cursor. After some quick research apparently there is this amazing free tool <a href="http://smoothmouse.com" target="_blank" rel="nofollow noopener noreferrer">Smoothmouse</a> which can simulate the mouse experience on Windows.</p>]]></description><link>https://blog.hao.dev/mouse-software-for-gaming-on-mac</link><guid isPermaLink="false">https://blog.hao.dev/mouse-software-for-gaming-on-mac</guid><pubDate>Sun, 22 Nov 2015 23:48:00 GMT</pubDate><content:encoded>&lt;p&gt;When using normal Windows mouse on a Mac, there were some weird acceleration applied to the cursor. After some quick research apparently there is this amazing free tool &lt;a href=&quot;http://smoothmouse.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;Smoothmouse&lt;/a&gt; which can simulate the mouse experience on Windows.&lt;/p&gt;&lt;p&gt;No offence to the Mac lovers, this is way better for gaming. So if you are used to gaming on Windows, then you will love this software.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Display Spreadsheet Tables On Website]]></title><description><![CDATA[<p>Came across this good library to display a large table on web pages. It is called <a href="http://handsontable.com" target="_blank" rel="nofollow noopener noreferrer">handsontable</a>, it is open source and seems very handy in some situations e.g. creating reports or needs to show large amount of data for some reason.</p>]]></description><link>https://blog.hao.dev/display-spreadsheet-tables-on-website</link><guid isPermaLink="false">https://blog.hao.dev/display-spreadsheet-tables-on-website</guid><pubDate>Sun, 22 Nov 2015 21:44:00 GMT</pubDate><content:encoded>&lt;p&gt;Came across this good library to display a large table on web pages. It is called &lt;a href=&quot;http://handsontable.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;handsontable&lt;/a&gt;, it is open source and seems very handy in some situations e.g. creating reports or needs to show large amount of data for some reason.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/2FOipktbWogAq4Sy0IKcwI/347989d2fee5966c17f76f68193de032/SpreadsheetTable.png&quot; alt=&quot;SpreadsheetTable&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Is Docker the new Vagrant]]></title><description><![CDATA[<p>I have been a massive Vagrant fan since I was introduced to it in 2013/2014. To me, it is the first time I stepped into the world of DevOps and see the huge difference it made to a team. Before Vagrant, ensuring team member's machines and the server environments software versions is a huge headache. We always ran into issues where code works fine on one person's computer and ends up breaking on another machine. This is terrible news when the whole deployment process is meant to be automated. My Team had no choice but to increase the amount of manual testing done on the website to reduce these kind of issues getting through to production.</p>]]></description><link>https://blog.hao.dev/is-docker-the-new-vagrant</link><guid isPermaLink="false">https://blog.hao.dev/is-docker-the-new-vagrant</guid><pubDate>Thu, 19 Nov 2015 18:23:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been a massive Vagrant fan since I was introduced to it in 2013/2014. To me, it is the first time I stepped into the world of DevOps and see the huge difference it made to a team. Before Vagrant, ensuring team member&apos;s machines and the server environments software versions is a huge headache. We always ran into issues where code works fine on one person&apos;s computer and ends up breaking on another machine. This is terrible news when the whole deployment process is meant to be automated. My Team had no choice but to increase the amount of manual testing done on the website to reduce these kind of issues getting through to production.&lt;/p&gt;&lt;p&gt;With the introduction of Vagrant, we were able to easily manage everyone&apos;s software version since everyone build their VM (virtual machine) based on the same configuration. This means software updates can be managed easily, everyone just need to rebuild their local VM using the updated config.&lt;/p&gt;
&lt;p&gt;So, what is Docker? Docker is not really an update to Vagrant, but instead of concept of VM. Since most of the time we don&apos;t need a full Linux VM to run our software. So Docker allows the ability to run a Docker container that is a stripped Linux OS, supporting its core functionalities but giving the user ability to install everything back if they wish. Docker doesn&apos;t quite replace Vagrant, since Vagrant was designed to be used for local development. Whereas Docker is aimed at server level optimisation. Of course, there is nothing stopping you from using Docker to do local development. Since the two products are meant to solve completely different issues, so I don&apos;t think they are in direct competition. But over time, the popularity of Docker may impact Vagrant. &lt;/p&gt;
&lt;p&gt;Personally, after moving away from PHP backend and more into Node.js I have also moved away from Vagrant. Maybe Docker will be more popular than Vagrant in the future, but for now neither is really my top choice.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[International postal address format]]></title><description><![CDATA[<p>I am currently working on a personal project which involves the user entering their address to be searched. I wanted to ensure this application is designed properly, so it can easily be scaled to work with international postal address. However, after some research it is still far from clear what format I should follow.</p>]]></description><link>https://blog.hao.dev/international-postal-address-format</link><guid isPermaLink="false">https://blog.hao.dev/international-postal-address-format</guid><pubDate>Wed, 18 Nov 2015 23:39:00 GMT</pubDate><content:encoded>&lt;p&gt;I am currently working on a personal project which involves the user entering their address to be searched. I wanted to ensure this application is designed properly, so it can easily be scaled to work with international postal address. However, after some research it is still far from clear what format I should follow.&lt;/p&gt;&lt;p&gt;Many answers online make suggestions based on US format, which is not very helpful for other countries with slightly different formats. I have included the address fields for my project below, I am sure for some countries this might not be 100% correct, but this is the best one I can come up with so far. Just FYI, I&apos;m mainly focusing on UK addresses here.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Address Line 1 (Room 15)&lt;/li&gt;
&lt;li&gt;Address Line 2 (Flat A)&lt;/li&gt;
&lt;li&gt;Address Line 3 (15 Street)&lt;/li&gt;
&lt;li&gt;City (London)&lt;/li&gt;
&lt;li&gt;Postcode (XX00 0XX)&lt;/li&gt;
&lt;li&gt;County (Middlesex)&lt;/li&gt;
&lt;li&gt;Country (United Kingdom)&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title><![CDATA[The different font types]]></title><description><![CDATA[<p>I have been working with different fonts for ages, but never understood the difference. Recently, I have been working on a project where we need to decide a list of fonts to support. After some very basic research, here is what I found out.</p>]]></description><link>https://blog.hao.dev/the-different-font-types</link><guid isPermaLink="false">https://blog.hao.dev/the-different-font-types</guid><pubDate>Wed, 11 Nov 2015 23:11:00 GMT</pubDate><content:encoded>&lt;p&gt;I have been working with different fonts for ages, but never understood the difference. Recently, I have been working on a project where we need to decide a list of fonts to support. After some very basic research, here is what I found out.&lt;/p&gt;&lt;p&gt;There are 3 different type of fonts, each with their unique properties and thus advantages:&lt;/p&gt;
&lt;p&gt;Serif fonts - Good for long paragraphs and text heavy pages
Sans-Serif fonts - Good for page headings
Monospaced fonts - Good for displaying code&lt;/p&gt;
&lt;p&gt;This will be useful to know when designing a poster or creating a web page in the future.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Development Technologies Landscape (2015)]]></title><description><![CDATA[<p>For a developer sometimes it can be difficult to know what to focus on in terms of languages, frameworks and libraries. I came across this diagram created by <a href="http://www.devnetwork.com" target="_blank" rel="nofollow noopener noreferrer">devnetwork</a>. It shows the top technology to focus on in different fields. Pretty useful way to measure yourself against, see what you already know and pick something new to learn in the future.</p>]]></description><link>https://blog.hao.dev/development-technologies-landscape-2015</link><guid isPermaLink="false">https://blog.hao.dev/development-technologies-landscape-2015</guid><pubDate>Wed, 11 Nov 2015 23:03:00 GMT</pubDate><content:encoded>&lt;p&gt;For a developer sometimes it can be difficult to know what to focus on in terms of languages, frameworks and libraries. I came across this diagram created by &lt;a href=&quot;http://www.devnetwork.com&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;devnetwork&lt;/a&gt;. It shows the top technology to focus on in different fields. Pretty useful way to measure yourself against, see what you already know and pick something new to learn in the future.&lt;/p&gt;&lt;p&gt;&lt;img src=&quot;//images.ctfassets.net/zsjhz5fgria0/6sWvGb8mOW6CAwkgMkIWog/0a1fbc58fee7180f47007ba04e2733c7/DevTechLandscape.png&quot; alt=&quot;DevTechLandscape&quot;&gt;&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Project tracking tool - wll.space]]></title><description><![CDATA[<p>Today, I discovered a new tool which is very similar to Jira and Trello. I feel Jira is designed mostly for complex projects but can be a bit overkill for smaller/personal projects. Trello is very simple and straightforward to use, however, it lacks the ability to scale as the project unfolds. From a brief look at wll.space is very similar to Trello in this regard, simple but lacks scalability. Its design is what differentiates from the other two well-known applications.</p>]]></description><link>https://blog.hao.dev/project-tracking-tool-wll-space</link><guid isPermaLink="false">https://blog.hao.dev/project-tracking-tool-wll-space</guid><pubDate>Fri, 30 Oct 2015 20:09:00 GMT</pubDate><content:encoded>&lt;p&gt;Today, I discovered a new tool which is very similar to Jira and Trello. I feel Jira is designed mostly for complex projects but can be a bit overkill for smaller/personal projects. Trello is very simple and straightforward to use, however, it lacks the ability to scale as the project unfolds. From a brief look at wll.space is very similar to Trello in this regard, simple but lacks scalability. Its design is what differentiates from the other two well-known applications.&lt;/p&gt;&lt;p&gt;If you want a more visual looking project tracking tool, I recommend giving &lt;a href=&quot;http://wll.space&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;wll.space&lt;/a&gt; a try.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Set up MEAN.js in Vagrant]]></title><description><![CDATA[<p>If you have used Vagrant already then, you must know how powerful it can be when it comes to sharing local development environments. If you have not used it already, the whole idea is one person from the development team sets up a VM on their local machine then configure it with all the required libraries, settings and correct software versions. This VM can then be packaged up and shared with anyone.</p>]]></description><link>https://blog.hao.dev/set-up-mean-js-in-vagrant</link><guid isPermaLink="false">https://blog.hao.dev/set-up-mean-js-in-vagrant</guid><pubDate>Sun, 11 Oct 2015 22:50:00 GMT</pubDate><content:encoded>&lt;p&gt;If you have used Vagrant already then, you must know how powerful it can be when it comes to sharing local development environments. If you have not used it already, the whole idea is one person from the development team sets up a VM on their local machine then configure it with all the required libraries, settings and correct software versions. This VM can then be packaged up and shared with anyone.&lt;/p&gt;&lt;p&gt;Why is this powerful? Well, you can ensure that everyone on your team has the exactly same development environment no matter which OS they are using. You can control and maintain the exact version of all the software. Rather than the whole team spend weeks trying to resolve issues on individual machines, it is done once and for all.&lt;/p&gt;
&lt;p&gt;It all sounds well and good, but it has its disadvantages too. Since it is a VM box, not all the setting up is straight forward. It is a VM after all, and sometimes the commands are little tricky. You are faced with issues which otherwise does not exist on real machines, and this happened to me during the setup process with my team. After many trial and error, I finally found the solution and want to share it here.&lt;/p&gt;
&lt;h2&gt;Installing MEAN.js&lt;/h2&gt;
&lt;p&gt;This section is for one person from the team to complete, because everything from here can be shared with the team. There are already excellent sources out there; I recommend this one: &lt;a href=&quot;http://thejackalofjavascript.com/vagrant-mean-box&quot; target=&quot;_blank&quot; rel=&quot;nofollow noopener noreferrer&quot;&gt;http://thejackalofjavascript.com/vagrant-mean-box&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I normally have a repository structure like this:&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;- root_folder
   - Vagrantfile
   - myApp&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;h2&gt;Issues and how to resolve them&lt;/h2&gt;
&lt;p&gt;The blog by arvindr21 has everything right, but the only problem I found was when anyone else on my team tries to pull from the repo and run the site they get a lot of errors.&lt;/p&gt;
&lt;p&gt;It turns out; it is not as simple as &lt;code class=&quot;language-text&quot;&gt;npm install&lt;/code&gt;. First of all, you have to run it as an admin user, so you have to do &lt;code class=&quot;language-text&quot;&gt;sudo npm install&lt;/code&gt;. Even if you run it as an admin user, you will still see errors along the lines of &apos;sys-link broken&apos;. This is a vagrant issue, to solve this you have to run &lt;code class=&quot;language-text&quot;&gt;sudo npm install --no-bin-links&lt;/code&gt;. Then you got to install git and update bower. I know all this sounds confusing, so please follow the instructions shown below.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: This is for members who are consuming the VM, and not the creator.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ol&gt;
&lt;li&gt;Pull the project from Github (or another repository). I am assuming the vagrant file is set up correctly e.g. private_network IP is &quot;11.12.13.14&quot;, and port 3000 forward is added. If not then add these two lines in your Vagrant file.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;config.vm.network &quot;private_network&quot;, ip: &quot;11.12.13.14&quot;
config.vm.network &quot;forwarded_port&quot;, guest: 3000, host: 3000&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;vagrant up; vagrant ssh&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Then go to the project folder in the VM, by running &lt;code class=&quot;language-text&quot;&gt;cd /vagrant/myApp&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Now set up dependencies by &lt;code class=&quot;language-text&quot;&gt;sudo npm install --no-bin-links&lt;/code&gt;. This process could take a few minutes, so take a quick tea break.&lt;/li&gt;
&lt;li&gt;Then install git, because you need it before the next step &lt;code class=&quot;language-text&quot;&gt;sudo apt-get install git&lt;/code&gt;. Type &lt;code class=&quot;language-text&quot;&gt;y&lt;/code&gt; and then enter to accept the confirmation.&lt;/li&gt;
&lt;li&gt;Once git is installed, do &lt;code class=&quot;language-text&quot;&gt;bower install&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Run &lt;code class=&quot;language-text&quot;&gt;grunt&lt;/code&gt; and wait for everything to be loaded and go to &lt;code class=&quot;language-text&quot;&gt;http://11.12.13.14:3000&lt;/code&gt; to see the site working.&lt;/li&gt;
&lt;/ol&gt;</content:encoded></item><item><title><![CDATA[Awesome Text Editor - Atom]]></title><description><![CDATA[<p>Since I started using Sublime Text 2, I have fallen in love with the simplicity design. Compared to other more "robust" integrated development environment (IDE), it was faster to run, more customisable, had a large community and some of the functionalities are simply awesome. Since Sublime Text requires a payment of $70, it is a little pricey for me. After I had enough with pop ups telling me to pay the licence fee, I decided to look for an alternative.</p>]]></description><link>https://blog.hao.dev/awesome-text-editor-atom</link><guid isPermaLink="false">https://blog.hao.dev/awesome-text-editor-atom</guid><pubDate>Sat, 10 Oct 2015 13:34:00 GMT</pubDate><content:encoded>&lt;p&gt;Since I started using Sublime Text 2, I have fallen in love with the simplicity design. Compared to other more &quot;robust&quot; integrated development environment (IDE), it was faster to run, more customisable, had a large community and some of the functionalities are simply awesome. Since Sublime Text requires a payment of $70, it is a little pricey for me. After I had enough with pop ups telling me to pay the licence fee, I decided to look for an alternative.&lt;/p&gt;&lt;p&gt;I have tried many IDEs and text editors, they all had problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Not available on all OS platforms&lt;/li&gt;
&lt;li&gt;Expensive licence/subscription fee&lt;/li&gt;
&lt;li&gt;Too bulky, and not as light as Sublime Text&lt;/li&gt;
&lt;li&gt;Too limiting, does not have all the key functionalities&lt;/li&gt;
&lt;li&gt;Lack the subtle touches of Sublime Text e.g. multi-select&lt;/li&gt;
&lt;li&gt;Does not have the massive community behind it to create plugins and improve the product in the long run&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There were probably more issues, but those are the main problems which prevented me from using them. Then I found out about Atom, an open source project backed by Github. At the time I first used it, it was slow, buggy and lacking many of the plugins from Sublime Text (hell it wasn&apos;t available for Windows). After a year, everything changed! Now I can recommend it to anyone, for me it doesn&apos;t have any problem, and I can be confident to say it is now a better Text Editor compared to Sublime Text. So if you haven&apos;t tried it already, I think you should spend a few days and play around with it!&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Installing linux on GUID Partition Table (GPT) disk]]></title><description><![CDATA[<p>There are many advantages of GPT disks compared to Master Boot Record (MBR). MBR was introduced back in 1983, any technology being that old has a restriction in modern day computing, and MBR is no other. If you are interested, feel free to read up on the comparison between MBR and GPT, but I won't be explaining the differences here. Instead, I want to explain the issues I had with GPT and how to resolve it.</p>]]></description><link>https://blog.hao.dev/installing-linux-on-guid-partition-table-gpt-disk</link><guid isPermaLink="false">https://blog.hao.dev/installing-linux-on-guid-partition-table-gpt-disk</guid><pubDate>Fri, 09 Oct 2015 21:53:00 GMT</pubDate><content:encoded>&lt;p&gt;There are many advantages of GPT disks compared to Master Boot Record (MBR). MBR was introduced back in 1983, any technology being that old has a restriction in modern day computing, and MBR is no other. If you are interested, feel free to read up on the comparison between MBR and GPT, but I won&apos;t be explaining the differences here. Instead, I want to explain the issues I had with GPT and how to resolve it.&lt;/p&gt;&lt;p&gt;I tried to dual boot my Windows ten desktop computer with a new Linux OS (Debian based). I started off with Elementary OS, but then found some of the bugs annoying and moved to Linux Mint. I had the same issues on both distribution, and I wasted a lot of time trying to fix it. So I would recommend not convert your hard disk to GPT and install Linux on it unless you know what you are doing.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Simplify SSH connectivity with a config file]]></title><description><![CDATA[<p>Managing a large number of servers can be annoying, but a config file will make it much easier. Here I talk about how a config file can make all the difference.</p>]]></description><link>https://blog.hao.dev/simplify-ssh-connectivity-with-a-config-file</link><guid isPermaLink="false">https://blog.hao.dev/simplify-ssh-connectivity-with-a-config-file</guid><pubDate>Thu, 08 Oct 2015 19:40:00 GMT</pubDate><content:encoded>&lt;p&gt;Managing a large number of servers can be annoying, but a config file will make it much easier. Here I talk about how a config file can make all the difference.&lt;/p&gt;&lt;h2&gt;Creating a config file&lt;/h2&gt;
&lt;p&gt;In order to SSH into a server, the IP address is required and needs to be specified in the terminal command.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ SSH user@1.2.3.4&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The above format is okay if you are only working on a single server, if you have to work with many servers, then managing all the IP address can quickly get out of hand. The beauty of a config file is that it allows you to map an IP address to your domain name. After the mapping, you can just do the following.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;$ SSH mydomain.com&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;If you don&apos;t have this file already, then create it first.&lt;/p&gt;
&lt;p&gt;&lt;code class=&quot;language-text&quot;&gt;touch ~/.ssh/config&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;Then copy the following template into the file, don&apos;t forget to update the relevant values (e.g. domain, IP and user).&lt;/p&gt;
&lt;div class=&quot;gatsby-highlight&quot; data-language=&quot;text&quot;&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Host yourdomain.com  
HostName 1.2.3.4
User root
IdentityFile ~/.ssh/id_rsa&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</content:encoded></item></channel></rss>