-
Notifications
You must be signed in to change notification settings - Fork 0
/
production.ndjson
13 lines (13 loc) · 215 KB
/
production.ndjson
1
2
3
4
5
6
7
8
9
10
11
12
13
{"_type":"post","_createdAt":"2024-02-05T07:35:07.322+02:00","publishedAt":"2024-02-05T07:35:07.322+02:00","title":"Enhance Your React App's Scalability using Storybook and Chromatic","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h1","children":[{"_type":"span","marks":[],"text":"Scale Your React App with Storybook and Chromatic"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Description:"},{"_type":"span","marks":["em"],"text":"This article explains integrating Storybook and Chromatic to scale your application's component library and benefit from clear documentation, visual regression testing and team efficiency."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"1. Why Use Storybook?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Storybook is an invaluable tool for engineers, product owners and stakeholders alike. Storybook allows Front-end Engineering teams to build component libraries to facilitate collaboration and prevent the development of components from being blocked by more significant project architecture decisions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It acts as a standalone application within your more extensive project that can document components and their variations. Storybook comes packed with a ton of features which can be customised and configured to your liking. Below are some of the features I use on everyday projects:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Web Accessibility audits."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Unit, interaction and snapshot testing."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Document component functionality for engineers and stakeholders."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Easy publishing and hosting."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Integration with Chromatic for VRT (Visual Regression Testing)."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This article explores installing and configuring Storybook in a sample Create React App project, installing addons, writing stories, generating automated documentation and publishing your Storybook to the web."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"2. Set up and Configure Storybook"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Installing Storybook"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Storybook was developed to fit into a plethora of different project types. The most effective way to get started with Storybook is to install it into a pre-existing application and run a simple command at the root of your project:"}]},{"_type":"code","code":"npx storybook@latest init ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The above command will look at your project dependencies and determine the most appropriate way to install Storybook."}]},{"_type":"block","markDefs":[{"_key":"8593c9299e10","_type":"link","href":"https://Storybook.js.org/docs/react/configure/frameworks"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you need help determining whether your project would support Storybook, read through the "},{"_type":"span","marks":["8593c9299e10"],"text":"Frameworks page"},{"_type":"span","marks":[],"text":" in the documentation."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"You can install Storybook manually, but this generally results in errors and mismanaged dependencies, which can cause problems."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Configuring Storybook"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One of the more complex aspects of Storybook is configuring it to align with technologies present in your application. Further customisation will be required to ensure Storybook behaves in a manner aligned with your applications technology stack."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Configuring Storybook is primarily done through the "},{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":" file. You can specify everything from how documentation is presented to extending Storybooks' UI with add-ons; you can even extend WebPack."}]},{"_type":"block","markDefs":[{"_key":"8bc0067ae21d","_type":"link","href":"https://Storybook.js.org/docs/react/configure/styling-and-css"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"Storybook supports TypeScript out of the box, but you must set up your CSS architecture. Many flavours of CSS are supported. You can find more information in the "},{"_type":"span","marks":["8bc0067ae21d"],"text":"Styling and CSS documentation"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's spin up a Create React App instance:"}]},{"_type":"code","code":"npx create-react-app my-scalable-component-library ","language":"bash"},{"_type":"block","markDefs":[{"_key":"e930070513d2","_type":"link","href":"https://create-react-app.dev/"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"The above command will bootstrap a basic React application - we'll be using "},{"_type":"span","marks":["e930070513d2"],"text":"Create React App"},{"_type":"span","marks":[],"text":" for this article, though other frameworks are also supported. Let's make sure your application is working correctly by running "},{"_type":"span","marks":["code"],"text":"npm run start"},{"_type":"span","marks":[],"text":" - you should see the following:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"img","asset":{"src":"react-splash-screen.jpg","alt":"A screenshot of Create React Apps initial screen"}}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's install Storybook next. Run the following line in the root of your application:"}]},{"_type":"code","code":"npx storybook@latest init ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The script will do a bit of thinking and then prompt you to confirm a few details. Storybook is smart enough to detect that we're using CRA (Create React App), and it will likely need to update a few dependencies to work seamlessly with your project. Hit "},{"_type":"span","marks":["code"],"text":"Y"},{"_type":"span","marks":[],"text":" when you see the below prompt:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"img","asset":{"src":"storybook-init.jpg","alt":"A screenshot of the output of running Storybook for the first time"}}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If all goes according to plan - Storybook will launch in your browser, and you'll see the following:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"img","asset":{"src":"storybook-splash-screen.jpg","alt":"A screenshot of a successfully installed Storybook in the browser"}}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"At this point, it's worth looking at what's changed in our project. Storybook added a "},{"_type":"span","marks":["code"],"text":".storybook"},{"_type":"span","marks":[],"text":" folder where your configuration files live. You'll also notice a "},{"_type":"span","marks":["code"],"text":"stories"},{"_type":"span","marks":[],"text":" folder added to the "},{"_type":"span","marks":["code"],"text":"src"},{"_type":"span","marks":[],"text":" directory. There are generally three related files for each \"story\". We'll uncover that in more detail a bit later."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Both the "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" and "},{"_type":"span","marks":["code"],"text":"package-lock.json"},{"_type":"span","marks":[],"text":" have been updated. Updates to these files pertain primarily to dependencies, but "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" also has two new scripts:"}]},{"_type":"code","code":"\"storybook\": \"storybook dev -p 6006\", \"build-storybook\": \"storybook build\" ","language":"json"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Run "},{"_type":"span","marks":["code"],"text":"npm run storybook"},{"_type":"span","marks":[],"text":" to spin up a dev environment and "},{"_type":"span","marks":["code"],"text":"npm run build-storybook"},{"_type":"span","marks":[],"text":" when you're ready to publish your first Storybook."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's run "},{"_type":"span","marks":["code"],"text":"npm run build-storybook"},{"_type":"span","marks":[],"text":" - the output is a folder called "},{"_type":"span","marks":["code"],"text":"storybook-static"},{"_type":"span","marks":[],"text":" - this is a \"published\" Storybook that can be made publicly accessible."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"At this point, Storybook and CRA are entirely set up. You may want to add the "},{"_type":"span","marks":["code"],"text":"storybook-static"},{"_type":"span","marks":[],"text":" folder to your "},{"_type":"span","marks":["code"],"text":".gitignore"},{"_type":"span","marks":[],"text":" if you do not plan on tracking the static files."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's also worth noting that a handful of components have been added to your directory - you can remove them if need be. However, I recommend keeping them for reference, if for nothing else. Before we move on, let's briefly take a look at "},{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":":"}]},{"_type":"code","code":"/** @type { import('@storybook/react-webpack5').StorybookConfig } */ const config = { stories: [\"../src/**/*.mdx\", \"../src/**/*.stories.@(js|jsx|mjs|ts|tsx)\"], addons: [ \"@storybook/addon-links\", \"@storybook/addon-essentials\", \"@storybook/preset-create-react-app\", \"@storybook/addon-onboarding\", \"@storybook/addon-interactions\", ], framework: { name: \"@storybook/react-webpack5\", options: {}, }, docs: { autodocs: \"tag\", }, statistics: [\"../public\"], }; export default config; ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"There are a few essential parts to this file. Firstly, the "},{"_type":"span","marks":["code"],"text":"stories"},{"_type":"span","marks":[],"text":" key tells the Storybook where to look for component stories. As you update your file/folder architecture in CRA, update these paths to avoid losing your stories in Storybook."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["code"],"text":"framework"},{"_type":"span","marks":[],"text":" is generally different for each project type. "},{"_type":"span","marks":["code"],"text":"docs"},{"_type":"span","marks":[],"text":" tells Storybook to document components automatically."}]},{"_type":"block","markDefs":[{"_key":"4adfcb8d5554","_type":"link","href":"https://Storybook.js.org/docs/react/configure/overview"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"I would highly recommend "},{"_type":"span","marks":["4adfcb8d5554"],"text":"reading the Configure page"},{"_type":"span","marks":[],"text":" in the Storybook docs for more information about what can be handled through "},{"_type":"span","marks":["code"],"text":"main.js"}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"3. Decide on Storybook Addons"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can think of Storybook addons as \"plugins\". They are pre-written packages that extend the core Storybook APIs and perform tasks like integrating JS/CSS Frameworks or enhancing the default behaviour of Storybook."}]},{"_type":"block","markDefs":[{"_key":"0bf5a434c94c","_type":"link","href":"https://Storybook.js.org/integrations/"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"What addons you install will depend on your project and your team's goals. There are effectively two types of addons, UI-based and Preset-based. \"UI-based\" add-ons customise the functional appearance of Storybook. \"Preset-based\" add-ons allow you to integrate with other technologies like TypeScript or Tailwind. You can find a collection of all add-ons on the "},{"_type":"span","marks":["0bf5a434c94c"],"text":"Integrations"},{"_type":"span","marks":[],"text":" page."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Note:"},{"_type":"span","marks":[],"text":" Some addons are maintained by the Storybook team, while others are community-driven. Community-driven addons may yield unexpected results or may not be compatible with the latest version of Storybook."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Before you go on the hunt for a collection of add-ons you feel will be best to integrate, make sure you take a look at what Storybook installs by default:"}]},{"_type":"code","code":"addons: [ \"@storybook/addon-links\", \"@storybook/addon-essentials\", \"@storybook/preset-create-react-app\", \"@storybook/addon-onboarding\", \"@storybook/addon-interactions\", ], ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"@storybook/addon-links"},{"_type":"span","marks":[],"text":" lets you link Stories to build prototypes."}]},{"_type":"block","markDefs":[{"_key":"04c7308ccdd8","_type":"link","href":"https://Storybook.js.org/integrations/tag/essentials/"}],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"@storybook/addon-essentials"},{"_type":"span","marks":[],"text":" include all add-ons located here: "},{"_type":"span","marks":["04c7308ccdd8"],"text":"https://Storybook.js.org/integrations/tag/essentials/"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"@storybook/preset-create-react-app"},{"_type":"span","marks":[],"text":" This is a Preset-based addon that enhances the integration with CRA."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"@storybook/addon-onboarding"},{"_type":"span","marks":[],"text":" - provides a guided tour of Storybook features."}]},{"_type":"block","markDefs":[{"_key":"f5d2ab64d92f","_type":"link","href":"https://Storybook.js.org/docs/react/writing-tests/interaction-testing"}],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"@storybook/addon-interactions"},{"_type":"span","marks":[],"text":" - allows you to debug the interaction state of your components. If you're interested, you can "},{"_type":"span","marks":["f5d2ab64d92f"],"text":"read more about Interaction tests"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's say you want to add another add-on to your configuration. Let's install the Accessibility add-on:"}]},{"_type":"code","code":"npm install @Storybook/addon-a11y ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Next, we need to tell Storybook to initialise the add-on; this can be done by adding the addon to the "},{"_type":"span","marks":["code"],"text":"addons"},{"_type":"span","marks":[],"text":" key in "},{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":":"}]},{"_type":"code","code":"addons: [ \"@storybook/addon-links\", \"@storybook/addon-essentials\", \"@storybook/preset-create-react-app\", \"@storybook/addon-onboarding\", \"@storybook/addon-interactions\", \"@storybook/addon-a11y\", ], ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Save "},{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":", and then let's boot up Storybook again by running "},{"_type":"span","marks":["code"],"text":"npm run storybook"},{"_type":"span","marks":[],"text":" :"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"img","asset":{"src":"accessibility-panel.jpg","alt":"A screenshot of the newly added Accessibility panel"}}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can see now that the \"Accessibility\" tab has been added to all story instances and is already flagging problems with a11y in our components. \"UI-based\" add-ons only require a little configuration. \"Preset-based\" can be more complex. The reason for this is that \"Preset-based\" addons often need further configuration, such as:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Placing configuration files at the root of your project"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Configuring options / Webpack configurations that must align with your CRA application settings."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This can cause a lot of friction, and remediation can depend on the framework of choice. Use caution when extending Storybook with \"Preset-based\" add-ons and ensure parity with your application."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"4. Write and Document Component Stories"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Now, it's time to write stories. A story in Storybook is usually tied to a component and its variations. Stories are highly dynamic files written in React, MarkDown, or a combination of both technologies. Stories are passed parameters that align with props the React component accepts."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"These props can be configured to output variations of each component. This allows engineers to interact with prop values within the Storybook UI. Let's review our "},{"_type":"span","marks":["code"],"text":"Button"},{"_type":"span","marks":[],"text":" story that was added through our bootstrapping of Storybook:"}]},{"_type":"code","code":"import { Button } from './Button'; export default { title: 'Example/Button', component: Button, parameters: { layout: 'centered', }, tags: ['autodocs'], argTypes: { background-colour: { control: 'color' }, }, }; export const Primary = { args: { primary: true, label: 'Button', }, }; export const Secondary = { args: { label: 'Button', }, }; export const Large = { args: { size: 'large', label: 'Button', }, }; export const Small = { args: { size: 'small', label: 'Button', }, }; ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The above illustrates the format you can follow when creating a Story. There must always be a default export. This is the main component. It's where essential settings are configured. Most of them are self-explanatory. However, I would like to call out the following:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"parameters"},{"_type":"span","marks":[],"text":": are a set of static, named metadata about a story, typically used to control the behaviour of Storybook features and add-ons"}]},{"_type":"block","markDefs":[{"_key":"7394be23b870","_type":"link","href":"https://Storybook.js.org/docs/react/writing-docs/autodocs"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"tags"},{"_type":"span","marks":[],"text":": will auto-generate documentation for each component story. Read more about "},{"_type":"span","marks":["7394be23b870"],"text":"AutoDocs"}]},{"_type":"block","markDefs":[{"_key":"8328232f0db4","_type":"link","href":"https://Storybook.js.org/docs/react/api/arg-types"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"argTypes"},{"_type":"span","marks":[],"text":": specify the behaviour of "},{"_type":"span","marks":["code"],"text":"args"},{"_type":"span","marks":[],"text":" or annotate args - "},{"_type":"span","marks":["8328232f0db4"],"text":"read more"},{"_type":"span","marks":[],"text":" about "},{"_type":"span","marks":["code"],"text":"archetypes"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Any named export after the initial default export is a variation. Each variation is an object with an args key. "},{"_type":"span","marks":["code"],"text":"args"},{"_type":"span","marks":[],"text":" in this context should align with the props passed to your component. Each variation will be output in Storybook under the \"Button\" component, and you can interact with them as needed."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Understanding "},{"_type":"span","marks":["code"],"text":"Decorators"},{"_type":"span","marks":[],"text":" goes a long way if you plan on building more complex stories. A decorator provides a way to wrap stories in extra context and functionality. Stories can be passed to a decorator by setting the "},{"_type":"span","marks":["code"],"text":"decorator"},{"_type":"span","marks":[],"text":" key in the story parameters:"}]},{"_type":"code","code":"decorators: [ (Story) => ( <div style={{ margin: '3em' }}> {/* 👇 Decorators in Storybook also accept a function. Replace <Story/> with Story() to enable it */} <Story /> </div> ), ], ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the above example, we add a "},{"_type":"span","marks":["code"],"text":"<div>"},{"_type":"span","marks":[],"text":" that wraps our component and assign it "},{"_type":"span","marks":["code"],"text":"3em"},{"_type":"span","marks":[],"text":" of "},{"_type":"span","marks":["code"],"text":"margin"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Stories can also consume components from other Stories. Just be mindful that this will rely on how your component renders and how much detail you plan on adding to your application overall."}]},{"_type":"block","markDefs":[{"_key":"7a99c582e2d4","_type":"link","href":"https://Storybook.js.org/docs/react/writing-stories/stories-for-multiple-components"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the Storybook docs, you can read about "},{"_type":"span","marks":["7a99c582e2d4"],"text":"Sub Components in detail"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Writing a Story for Our Application"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's put this all to the test. We're going to add a component to our application called "},{"_type":"span","marks":["code"],"text":"Footer"},{"_type":"span","marks":[],"text":" - this will require three files:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"Footer.jsx"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"Footer.stories.js"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["code"],"text":"footer.css"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Create those files in the "},{"_type":"span","marks":["code"],"text":"src/stories"},{"_type":"span","marks":[],"text":" folder. Our "},{"_type":"span","marks":["code"],"text":"Footer.jsx"},{"_type":"span","marks":[],"text":" is going to be simple:"}]},{"_type":"code","code":"import React from 'react'; import PropTypes from 'prop-types'; export const Footer = ({ siteOwner, showCopyRight }) => ( <footer> <div className=\"footer\"> {showCopyRight && ( <div> <span> <span role=\"img\" aria-label=\"copy\">©️</span> 2018 {siteOwner}. </span> </div> )} </div> </footer> ); Footer.propTypes = { siteOwner: PropTypes.string.isRequired, showCopyRight: PropTypes.bool, }; Footer.defaultProps = { showCopyRight: true, }; ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It takes two props. "},{"_type":"span","marks":["code"],"text":"siteOwner"},{"_type":"span","marks":[],"text":" and "},{"_type":"span","marks":["code"],"text":"showCopyRight"},{"_type":"span","marks":[],"text":". Next up, let's write a story for the Footer:"}]},{"_type":"code","code":"import { Footer } from './Footer'; export default { title: 'Example/Footer', component: Footer, tags: ['autodocs'], parameters: { layout: 'fullscreen', }, }; export const WithSiteOwner = { args: { siteOwner: 'Jane Doe', showCopyRight: true, }, }; export const WithOutCopyRight = { args: { siteOwner: 'Jane Doe', showCopyRight: false, }, }; ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is a relatively contrived example, but it illustrates how easy it is to add a story. Add the above to "},{"_type":"span","marks":["code"],"text":"Footer.stories.js"},{"_type":"span","marks":[],"text":" - the result will be an auto-documented, multi-variant story that allows users to interact with the component's props."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"img","asset":{"src":"footer-story.jpg","alt":"A screenshot of the Footer story recently added to Storybook"}}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Try updating the "},{"_type":"span","marks":["code"],"text":"siteOwner"},{"_type":"span","marks":[],"text":" value directly in the story controls. If you want to style the Footer, import "},{"_type":"span","marks":["code"],"text":"footer.css"},{"_type":"span","marks":[],"text":" into "},{"_type":"span","marks":["code"],"text":"Footer.jsx"},{"_type":"span","marks":[],"text":" and reference the class names; if you're interested in seeing how the component behaves in your React application, import it:"}]},{"_type":"code","code":"import { Footer } from './stories/Footer'; ... <Footer siteOwner='Daine Mawer' showCopyRight /> ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Nice! You've just created your component and a corresponding story! Next, we'll discuss publishing your storybook on the web."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"5. Publish Your Storybook"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Engineers can easily view and develop locally on Storybook - as the configuration is tracked through your preferred version control system. However, a URL to access the published Storybook would be far more manageable for non-technical stakeholders."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When you run a production build of Storybook, its output is a collection of static files that are outputted into a build folder. Thankfully, the Storybook team has made this relatively easy to achieve. All you need to do is run:"}]},{"_type":"code","code":"npm run build-storybook ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The build will terminate if it comes across any build errors, so there's no way of publishing a broken Storybook. Now that we have a production build, we need somewhere to host it."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"There are several ways to do this: GitHub Pages, Netlify, and AWS S3. Some of these options require more configuration than others."}]},{"_type":"block","markDefs":[{"_key":"d11a150aa9c8","_type":"link","href":"https://Storybook.js.org/docs/react/sharing/publish-Storybook#github-pages"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you don't plan on setting up Chromatic (recommended), I recommend running with Github Pages as you can add a "},{"_type":"span","marks":["d11a150aa9c8"],"text":"Github Action"},{"_type":"span","marks":[],"text":" to make short work of the configuration and set up."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"6. Setup Chromatic for VRT"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Chromatic is a powerful tool that lives alongside Storybook. The Storybook team maintains Chromatic, and thus, integrating the tool into your pre-existing application and CI requires minimal effort."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"By integrating Chromatic, you can be sure that visual regressions, even interaction bugs, do not make it to your production environment. The application allows for seamless collaboration within teams and goes a long way to ensuring bugs are caught early and often."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"What's more, Chromatic is free, with limitations, of course."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's say we want to integrate Chromatic with our published Storybook. Sign up for a Chromatic account and grab a Project Token. Next, you'll need to install the Chromatic NPM package into your project:"}]},{"_type":"code","code":"npm install --save-dev chromatic ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Then add a "},{"_type":"span","marks":["code"],"text":"chromatic"},{"_type":"span","marks":[],"text":" script to your "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" :"}]},{"_type":"code","code":"\"scripts\": { \"chromatic\": \"chromatic\" } ","language":"json"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You'll then need to ensure that you have a "},{"_type":"span","marks":["code"],"text":".env"},{"_type":"span","marks":[],"text":" file with the following environment variable defined: "},{"_type":"span","marks":["code"],"text":"CHROMATIC_PROJECT_TOKEN"},{"_type":"span","marks":[],"text":" - you can add the Project Token from your Chromatic account as the value."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Run "},{"_type":"span","marks":["code"],"text":"npm run chromatic"},{"_type":"span","marks":[],"text":". This will publish your Storybook to your Chromatic project. You can then access an impressive UI to review components and their changes. The problem with this setup is that you'll need to run Chromatic each time you change components."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This may be okay for smaller projects, but committing changes to your application and having a CI pipeline handle this hard work is far better - Chromatic CI integrates directly into Pull Requests."}]},{"_type":"block","markDefs":[{"_key":"b85104a3697a","_type":"link","href":"https://www.chromatic.com/docs/github-actions/"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you're using Github, you can quickly get up and running with Github Actions by adding a "},{"_type":"span","marks":["code"],"text":"workflows"},{"_type":"span","marks":[],"text":" folder to your "},{"_type":"span","marks":["code"],"text":".github"},{"_type":"span","marks":[],"text":" directory. Follow the steps in Chromatic Docs outlined at "},{"_type":"span","marks":["b85104a3697a"],"text":"Automate Chromatic with GitHub Actions"},{"_type":"span","marks":[],"text":" to get up and running."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You have now published a component library that runs UI Tests and Reviews each time you commit changes to your components."}]},{"_type":"block","markDefs":[],"style":"h2","children":[{"_type":"span","marks":[],"text":"7. Conclusion and Takeaways"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Storybook and Chromatic tools will empower your team to deliver higher-quality code. Use Storybook to automate and iterate on your shared component libraries."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Above all else, these two tools will ensure your engineering team can develop in confidence, ship features and bug fixes more efficiently and ensure that your product is always well-documented, scalable and extensible."}]},{"_type":"block","markDefs":[{"_key":"764a0d99d7ec","_type":"link","href":"https://www.sitepoint.com/scale-react-app-storybook-chromatic/"}],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"Read the original article on "},{"_type":"span","marks":["em","764a0d99d7ec"],"text":"Sitepoint"}]}]}
{"_type":"post","_createdAt":"2023-01-18T07:35:07.322+02:00","publishedAt":"2023-01-18T07:35:07.322+02:00","title":"Five Front-end File Architectures For Better Code Organisation","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Choosing the most appropriate file architecture can set you up for massive success when building scalable front-ends. There are a variety of approaches to file architectures, each with its pros and cons. In this article, I will discuss the benefits and trade-offs of 5 common front-end file architectures to help you decide how to scale your next project."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Flat Architecture"}]},{"_type":"code","code":"|- index.html |- style.css |- analytics.js |- about.html |- modernizer.js ","language":"fileconvention.bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Opting for the flat-file architecture is by far the most straightforward approach. This architecture dictates that all your files are stored in a single directory. This architecture works well on micro to small projects, but as the project scales, the number of files and file types tend to grow out of control."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As no hierarchy is explicitly set, the cognitive load on collaborators can cause frustration and lead to uncontrolled and wild additions to the project which do not follow rhyme or reason."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Benefits of the "},{"_type":"span","marks":["strong","em"],"text":"Flat Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Quick and simple to get started."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Can easily be hosted on GitHub Pages or Amazon S3."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Easy version control."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Virtually no overhead to understanding the semantics of the architecture."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Downfalls of the "},{"_type":"span","marks":["strong","em"],"text":"Flat Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Scalability becomes seriously limited."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Multiple file types exist in an unordered and disorganised fashion."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Has the potential to lead to developer overhead in trying to determine where functionality exists."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Hierarchical Architecture"}]},{"_type":"code","code":"|- css/ |- javascript/ |- assets/ |- images/ |- fonts/ |- index.html ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In a hierarchical file architecture, a project is organised into multiple directories. This allows engineers to manage code better and makes it easier to determine where specific functionality is stored within the project. Generally, directories include:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"CSS"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"JavaScript"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Assets (Images, Fonts etc.)"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Each folder can then be further broken down into more semantic folder structures. For instance, within your CSS folder, you may have a folder structure that looks something like this:"}]},{"_type":"code","code":"|- css/ |- base/ |- components/ |- typography/ ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Benefits of the "},{"_type":"span","marks":["strong","em"],"text":"Hierarchical Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Added scalability when compared to the "},{"_type":"span","marks":["em"],"text":"Flat Architecture."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Better organisational semantics."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Less developer overhead as file types are generally grouped."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Logical and predictable organisation."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Downfalls of the "},{"_type":"span","marks":["strong","em"],"text":"Hierarchical Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"This architecture can be more complex."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It can be opinionated - there's no \"right\" way to do it."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It can be slower at scale as servers may need to traverse multiple folder hierarchies."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Modular Architecture"}]},{"_type":"code","code":"|- index.html |- modules |- ExampleModule |- components |- tests |- helpers |- mocks |- services |- types ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Using a Modular architecture allows engineers to self-contain functionality. Each module folder contains everything about or relating to that module - from components to type declarations. Engineers can then easily update multi-dimensional aspects of a particular module, improving the developer experience and allowing for faster iterations as all functionality remains closely placed and organised."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Another benefit of the modular architecture is that it provides a high-level overview of a site's functionality; this allows developers to understand the project's scope and reduces cognitive load."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Benefits of the Modular Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"This architecture allows for reusable components."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It can scale with ease as additional modules seemingly fit into place."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Easier to test and maintain over the long term."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"More opportunities to use code splitting."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Downfalls of the Modular Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"This architecture can become complex."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It generally has a dependency on a build tool or task manager."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Heavily relies on developers maintaining components in a standardised way."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Comes with a slightly higher learning curve."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Component-based Architecture"}]},{"_type":"code","code":"|- src |- components |- Header |- index.jsx |- Header.module.css ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One of the more common and well-known architectures, the Component-based architecture, will be familiar if you've spent time building React-based applications. This architecture intends to ensure complete usability."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The architecture allows for scalable, individual components and provides an excellent platform for more complex features that consume smaller pieces within the architecture. This architecture works best with frameworks like React or Vue that leverage a component-based methodology."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Benefits of the Component-based Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Reusability is front and centre."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"More efficient maintenance and development efforts."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Easy to test."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Better performance overall due to code splitting and the potential to load components dynamically."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Clear developer experience that can quickly be adopted."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Downfalls of the Component-based Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"The component-based architecture can be complex, especially when using a build tool like Webpack."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It comes with a relatively high learning curve for engineers."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"The architecture can lead to over-engineering at times."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Navigating the code base can become complicated because multiple smaller files contain less functionality."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Feature-based Architecture"}]},{"_type":"code","code":"|- src |- features |- blog |- shop |- cart |- components |- pages |- services ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The featured-based architecture organises a code base by features rather than file types or components. This can make it easier for engineers to understand how project functionality fits together."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Generally, features are split into directories containing all the functionality required to implement and execute that feature. As you can see in the example above, large pieces of functionality, i.e. blog, shop, cart, are contained within their directories, and all related functionality is contained within further sub-directories."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This architecture works well if the project management of the site is centred around delivering significant and specific features."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This architecture is still used today by more monolith-like frameworks such as Laravel and WordPress. It allows for easy organisation and an orientating developer experience."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Benefits of the "},{"_type":"span","marks":["strong","em"],"text":"Feature-based Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Easy organisation, as features and related functionality exist in one easy-to-find place."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Features can easily be added and removed."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Easy to add tests and maintain over the long term."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Provides for a better developer experience."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Isolation of components ensures that functionality is safeguarded."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["strong"],"text":"Downfalls of the "},{"_type":"span","marks":["strong","em"],"text":"Feature-based Architecture"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It can be a steep learning curve for onboarding developers."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Requires a standardised approach to programming design patterns."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Over-engineering is a potential concern."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Code can overlap at times, especially if certain functionality is shared."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"This can lead to lots of smaller files needing more functionality."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Choosing a file architecture for your project is by no means a simple process. Always put scalability first when it comes to making such a decision. It is also essential to consider developer experience, how often features will be developed and extended, and how easy it is to add and remove elements. Also, different tech stacks may have a preferred architecture - for instance, a best practice file architecture for a React-based project may differ from WordPress-based project."}]}]}
{"_type":"post","_createdAt":"2023-01-20T07:35:07.322+02:00","publishedAt":"2023-01-20T07:35:07.322+02:00","title":"4 Surprising Benefits of Intermittent Fasting for Software Engineers","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I spent the last few months of 2022 integrating intermittent fasting into my daily routine. Fasting has gained popularity over recent years as a way to improve health and well-being. In essence, it's a behavioural pattern that requires individuals to only eat for specific \"windows\" during the day. I've been experimenting with the 16/8 plan; this means I do not eat food for 16 hours a day and then eat pretty much what I want (within reason) for an 8-hour window, which finishes around 20:00."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Effectively, you land up cutting out breakfast and a morning snack. Intermittent fasting has been shown to have several benefits for the human body."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Software engineers, plagued by the stereotype of poor eating and collecting bad habits, can uncover some surprising benefits to health and lifestyle when incorporating fasting into their daily routine."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In this article, I'll run through four essential benefits of intermittent fasting and how they can add value to your life."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Increased Cognitive Function"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The most significant benefit of intermittent fasting is improved cognitive function. When you fast, your body increases the production of a protein called *Brain-Derived Neurotrophic Factor (*BDNF), which is associated with improved memory and learning. As any software engineer will tell you, memory and learning come in handy when writing complex code. Software Engineers rely heavily on their mental sharpness and problem-solving skills to do their job and stay on top of their game. More than that, fasting has been shown to increase focus and concentration."}]},{"_type":"block","markDefs":[{"_key":"de514ede9b86","_type":"link","href":"https://www.youtube.com/watch?v=9tRohh0gErM"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"I'm an avid fan of Dr Andrew Huberman's Podcast, "},{"_type":"span","marks":["em"],"text":"Huberman Labs"},{"_type":"span","marks":[],"text":". He has done a great episode on the "},{"_type":"span","marks":["de514ede9b86"],"text":"Effects of Fasting & Time Restricted Eating on Fat Loss & Health"},{"_type":"span","marks":[],"text":" if you want to discover more scientific details about intermittent fasting."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"From my own experience, I feel like I've experienced improved mental capacity and function for months now. For the first week or so, you are distracted by the \"hunger\" feeling, but your body eventually adapts. When you feel that tinge of hunger, it's not always because you're hungry - generally, it's because you haven't had enough water, or it's habitual."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Better Time Management"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Perhaps not a direct side effect of fasting, but a benefit nonetheless is the change in time you feel when you begin fasting. You'd be surprised by how much time you spend thinking about, planning and making food during the day. As fasting limits that window to 8 hours in 24 hours, what you eat and when you eat it is dramatically reduced to a small window - generally lunch and dinner."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"After a few weeks of fasting - your cravings for unhealthy snacks disappear, and the amount of time you spend eating and planning snacks/meals reduces dramatically. I've always been a morning person, so I've found that fasting helps me keep my morning routines and rituals incredibly streamlined and focused."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When you re-focus the time you spend thinking about food, on food items that keep you full longer - nuts, avocado and Low GI items you can stay full longer, eat less, remain healthy and feel better."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Less Stress and Lighter Mood"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A direct result of fasting is that your body produces less cortisol. Cortisol is a hormone that is generally associated with stress and anxiety. If your body is making less of it, you should naturally feel a reduction in both symptoms. As a double-whammy, your body simultaneously increases the production of endorphins (the happy hormone) which can lift your mood and make you feel happier."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Although I cheat to a degree with my fasting routine (I have black coffee with a bit of honey when I wake up) - I've also found that fasting has helped me curve my need to drink too much coffee every day. 4 cups of coffee, in terms of caffeine, mainly after noon is enough to heighten anyone stress/anxiety response. I'm down to 2 cups daily and don't drink coffee past 1 PM."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I've also found that my afternoon sugar crash has disappeared. I noticed this within the first few days of fasting. I have always struggled with the afternoon crash; feeling sluggish, tired, and lethargic was a reality around 4 PM every day. I now feel much better, and though I don't feel like I have \"more\" energy, I think it's sustained more these days."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Multiple Health Benefits"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I'm not a medical professional, and I'm sure for everything I list here, there will be an argument against it, but some documented health benefits of controlled, intermittent fasting are:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Weight-loss"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Reduced inflammation"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Lower blood pressure"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Improved cholesterol levels"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The benefits will vary from person to person, and other than weight loss, I have yet to track the other 3 with any seriousness. I think these benefits are less about the fasting itself and more about the fact that you are simply consuming less \"bad\" food or food that your body can't use."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Intermittent fasting is a journey - you can decide whether it's the right fit for your lifestyle. Through my own experience, I've found it to provide considerable benefits to my routine, not only physically but mentally, as well as financially (buying less food, fewer snacks etc.)."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Fasting has many potential benefits: reducing inflammation, lower stress levels, weight loss, and overall well-being. For software engineers who sit all day, focused and passionate about their work, fasting is a valuable practice to introduce into your day-to-day that doesn't require significant commitments or time to see actual, healthy results."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"I'll stress again that I have no medical training and that you should consult your doctor before introducing intermittent fasting into your routine."}]}]}
{"_type":"post","_createdAt":"2023-02-13T07:35:07.322+02:00","publishedAt":"2023-02-13T07:35:07.322+02:00","title":"4 Tips for Properly Using the Return Statement in Your Code","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The return statement is an often overlooked programming paradigm. It allows you to specify a value to be returned by a function and signals the end of its execution. Using this statement properly can ensure your code runs smoothly and may prevent unexpected bugs. Here are five tips for using the return statement effectively:"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"1. Use the return statement to end a function or method"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The return statement signals the end of a function or method. When the return statement is encountered, the function immediately terminates:"}]},{"_type":"code","code":"function sayMyName(name) { console.log(name); return; // end the function } ","language":"js"},{"_type":"block","markDefs":[{"_key":"af2215da2b31","_type":"link","href":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#automatic_semicolon_insertion"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's worthwhile noting that the return statement is affected by ASI "},{"_type":"span","marks":["af2215da2b31"],"text":"Automatic Semicolon Insertion"},{"_type":"span","marks":[],"text":". This means that you cannot create a new line after the return statement is called:"}]},{"_type":"code","code":"/* Incorrect - would return nothing and flag an error. */ return; name; /* Correct - would return the value of name. */ return ( name; ); ","language":"js"},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"2. Return a value of the correct data type"}]},{"_type":"block","markDefs":[{"_key":"ee28a5de7aee","_type":"link","href":"https://developer.mozilla.org/en-US/docs/Glossary/Type_coercion"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"When using the return statement, make sure to return the correct data type of the intended value. If you fail to return the correct data type, your program will behave unexpectedly and may result in errors. This is due to a feature of JavaScript known as "},{"_type":"span","marks":["ee28a5de7aee"],"text":"Type Coercion"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Look out for my articles on Using TypeScript and Getting Started with JavaScript Unit Testing to learn more about how to work around this pitfall confidently."}]},{"_type":"code","code":"function add(a, b) { return a + b; // correct - returns a number } function subtract(a, b) { return a - b; // correct - returns a number } function multiply(a, b) { return `${a} times ${b} is equal to: ${a * b}`; // incorrect - returns a string } ","language":"js"},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"3. Return a value that is appropriate for the function"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When writing functions, it is essential to consider what information you need from your data and how it can affect your code. For example, if your function needs to calculate the average of a list of numbers, returning the sum of those numbers would not be correct."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Make sure you carefully consider what information you need when writing functions and how it can affect your code."}]},{"_type":"code","code":"function average(numbers) { let total = 0; for (let number of numbers) { total += number; } return total; // incorrect - returns the sum of the numbers, not the average } function average(numbers) { let total = 0; for (let number of numbers) { total += number; } return total / numbers.length; // correct - returns the average of the numbers } ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When writing a function, it's important to be intentional about the following:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"What the function is intended to do"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"What data the function is intended to return"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Is the data returned immutable?"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"4. Use multiple return statements if necessary"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Sometimes, you may want to return a value immediately if certain conditions are met. This would apply to using if statements, as well as switch statements."}]},{"_type":"code","code":"function options(option) { switch (option) { case \"DnD\": return \"do-not-disturb\"; case \"Silent\": return \"silent-mode\"; default: return \"no-option-selected\"; } } function getOptions(option) { if (option === \"DnD\") { return \"do-not-disturb\"; } return option; } ","language":"js"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the options function above, we can omit the break keyword, along with a temporary variable to store the value if we explicitly return within a case - this makes code more readable and saves memory."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the getOptions function, we can also always ensure that we return a value regardless of whether a condition is met. If the condition is true, it will execute the return statement within the if - if not, it simply ignores that statement and returns the value of option"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"By following these tips, you can use the return statement effectively and ensure that your code runs smoothly and correctly. Proper use of the return statement is an essential part of good programming practice, and taking the time to get it right can save you a lot of headaches in the long run."}]},{"_type":"block","markDefs":[{"_key":"c99d89f27edd","_type":"link","href":"https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/return"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can "},{"_type":"span","marks":["c99d89f27edd"],"text":"read more about the return statement"},{"_type":"span","marks":[],"text":" on MDN."}]}]}
{"_type":"post","_createdAt":"2018-02-21T07:35:07.322+02:00","publishedAt":"2018-02-21T07:35:07.322+02:00","title":"Best Front-End Development Extensions for Visual Studio Code","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Visual Studio Code is one of the newest kids on the IDE “block” and it’s making a lot of noise. It’s taken a little while to gain traction, perhaps because it’s one of Microsoft’s few open source products, but it’s taken the open source community by storm. Developers who once swore their allegiance to Sublime Text 3 and Atom are slowly being converted by the power and simplicity of VS Code–not to mention its incredible library of extensions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I began my own coding life on Sublime Text 3, moved to Atom, then PHPStorm, then after my subscription expired with JetBrains I thought I’d give VS Code a try. It continues to make my life easier and by doing so, makes me a better developer."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This introduction will take you from zero to hero, getting you up and running with VS Code, focusing specifically on my favorite front-end development extensions, making sure that every aspect of your workflow is covered. I’m going to assume a few points for this article:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Your front-end development stack is not opinionated. I’m going to assume that at some point you will have the need for jQuery, ES6, React, Vue, PostCSS, or whatever floats your boat."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"You’re using Git for version control."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"You already have Node and NPM installed and setup correctly."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Some of the extensions listed below require some configuration outside of VS Code, I’ll let you know what does and doesn’t, but if you need any help, feel free to ask!"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Installing"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"First things first, if you don’t already have VS Code installed, head over to Visual Studio Code’s website. VS Code is cross-platform so our configurations will work on Windows, Mac and Linux."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"VS Code is also available as a Homebrew Cask package on Mac: brew cask install visual-studio-code"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A step that I find a lot of tutorials often leave out is the ability to execute VS Code from the terminal. There are a few ways of doing this. If you already have a .bash_profile setup you can add this:"}]},{"_type":"code","code":"export PATH=\"$PATH:/Applications/Visual Studio Code.app/Contents/Resources/app/bin\" ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Or, for a more cross platform approach, hit the Command Palette shortcut: Shift + Command + P and type the word shell - this will give you an option called: Install code command in PATH - hit enter and you’re done. Now, you can launch files and folders anywhere from the terminal by typing: code path/to/file/or/dir"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Linting"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Lint your code–it’s the best way to stop errors before they cause you any undue stress!"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"ESLint"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you’re running the latest version of ECMA Script, then ESLint is for you. This extension integrates the ESLint pattern recognizer straight into VS Code and aids you with common mistakes that developers make with the new syntax. It does require that you have the NPM ESLint package installed either locally in your project or globally."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"SASS Lint"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For those of you who enjoy writing more programmatic Sass, Sass Lint provides you with an easy to use set of configurations for writing standards-compliant Sass."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"JSHint"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"JS Hint is another great JavaScript Linter that aids you with logic, syntax and more, depending on your configuration."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"TSLint"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you’re a TypeScript fan, then the TSLint extension is your best friend. With TypeScript being a precompiled language, you have full control over what the compiler lints for you as well as auto formatting options."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"These extensions all offer Git friendly ways of managing custom / shared configurations between developers on your team namely in .eslintrc and .sass-lint.yml or .jshintrc"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I’ve purposely left our HTML Linters here. Whereas I’ll use an extension like Beautify for formatting and tabbing my HTML according to an .editorconfig I find that HTML Linters perform quite poorly. They don’t consider semantics and tend to do a bad job of helping you with accessibility concerns. As the meaning of HTML can be relatively ambiguous, it’s better to use a set of tools that do smaller jobs."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To extend the HTML functionality in VS Code I use the following:"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"AutoClose Tag"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It could use a bit of improvement, but I always liked Sublime Text 3’s auto closing tag functionality. The above mentioned extension will help you out with this in VS Code."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"AutoRename Tag"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is another useful HTML extension which allows users to click inside an HTML element and rename it, while simultaneously renaming the closing tag."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Automation"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I moved away from Grunt sometime ago, but coincidentally there don’t seem to be any Grunt extensions on the marketplace, at least not with enough traction to make much of an impact. The extensions below are mainly focused on Gulp and Webpack as they seem to be leading the race in the task management world right now."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Gulp Snippets"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This extension allows you to use the Command Palette in VS Code for easily injecting useful Gulp configurations into your Gulpfile.js; a must have if you’re still a little shaky about setting up Gulp."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Webpack"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Setting up Webpack can be one of the scariest tasks a developer has to commit to. The Webpack extension for VS Code takes the fear out of it by providing you with a minimal webpack.config.js to start your project. If you’re a progress person, and you don’t like being left in the dark while your Terminal thinks about things, then the Webpack Progress extension is for you. It provides a nice progress bar for when Webpack is doing its thing."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Git"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One of the most powerful extensions for version control in VS Code is:"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Git Lens"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For newcomers and advanced users alike, Git Lens is powerhouse. It makes an easy job of making Git and its many intricacies more manageable and visual. I’ve enjoyed using it so much that other than using the Terminal to add, commit, branch and push, I’ve completely scrapped Git GUI’s."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Almost every setting is customizable. Git Lens provides real-time feedback of Git data while you’re coding. Want to know who wrote that function that isn’t working? Want to see how much the code has changed over the last few commits? Not sure how to handle a merge conflict in a file that you never wrote, or have little context of? Git Lens handles this all for you."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Language Support and Intellisense"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Where as other IDEs have full on support for the majority of languages, VS Code leaves it up to you for the most part. As and when you require it, you can add Language Support for whatever technology you’re dealing with, out-of-the-box HTML and CSS, as well as JavaScript are provided, but if you have need for Python it’s just a click away. In my workflow I use Python every so often, but Node was a big thing for me."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Intellisense essentially takes care of completions. You can hit tab or hover over a file path, for instance, and VS Code will do the hard work for you."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Babel ES6 / ES7"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you’re one of those people that loves using new technologies in your workflow and you’re crazy about the new edition of JavaScript then the Babel ES6 / ES7 linter is for you."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"File System Path This is a brilliant extension that autocompletes / suggests file paths while you’re typing. If you come from a PHPStorm background, you’ll know the feeling!"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"NPM"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A great extension when using require() or import {} in Node.js, this extension autocompletes file paths to Node Modules."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"CSS Class Names"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is a very useful extension for auto completing CSS class names defined in your linked CSS files. If you’re a Bootstrap, Foundation or just a framework fan in general, this will save you a lot of time!"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"SCSS"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For the precompiled CSS fans, this extension makes intellisense possible for imports, mixins, includes and functions in SCSS."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"PostCSS"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you’ve yet to come across PostCSS then you’re missing out. It’s an advanced and extremely forward thinking set of plugins for CSS that makes CSS a lot more powerful. I use two PostCSS extensions: Syntax and Sorting; one enables syntax support for new CSS Level 4 modules like nesting and the other allows me to sort CSS properties alphabetically."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Vue"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"While not really Intellisense alone this is a great extension for Vue.js development. It’s an all-in-one solution for adding linting, intellisense, and formatting to Vue.js development and already includes some of the extensions I've mentioned above."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"React / React Native"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is a full solution for developing React Native applications. By default, VSCode has a ton of support built in for React."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Accessibility and Health"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You spend a lot of time in front of your IDE, so making sure it’s easy on the eyes and legible is just as important as any other aspect I’ve mentioned."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"VS Code Icons"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"First, get some folder/file icons so that you can easily distinguish the files you’re using."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Cobalt 2 Theme from Wes Bos"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Cobalt 2 is not too dark, not too light, and has great contrast for an editor theme. It also has a corresponding colour setup for ZSH Terminals."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Formatting"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Beautify"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As I mentioned earlier, the Beautify extension is a great tool if you’re adamant about code formatting and editor standardization. It also allows you to set a .jsbeautifyrc file which you can commit to Git repos for collaboration. Beautify can also be used as a replacement for .editorconfig if you don’t support it. Beautify formats JS, CSS, Sass, JSON and HTML."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"EditorConfig"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"More and more IDEs are providing support for EditorConfig, which allows you to include an .editorconfig in your project repos. This gives your fellow developers IDEs and linters to read from one file to standardize tabbing and spacing as well as line ending across projects."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Prettier"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For a more robust solution, try out Prettier. Prettier follows the same configuration paradigm as ESLint, providing you with a .prettierrc file which an be committed to repositories. It will also read from the .editorconfig file if it exists. In fact you can get rid of ESLint and Sass Lint and use Prettier as your one stop solution. It has its own extensions for EsLint, StyleLint and more."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Terminal"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I recently moved over to ZSH which has completely transformed my terminal experience. Whilst not being a direct extension of VS Code, it can be integrated through the Terminal pane. I had to do quite a bit of configuration to get it working on Mac, especially with colors and themes."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"ZSH features functionality like tab completion for directories, files, Git branches and more. You can read more about setting up ZSH on the Github repo or leave me a message in the comments below if you’d like to learn more."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Debugging"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Visual Studio Code comes with Debugging JavaScript right out of the box. You can take it a step further using the following extensions:"}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Debugger for Chrome"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This extension allows a direct sync between the VS Code Debugger and Chrome Developer Tools allowing you to set breakpoints and jump straight into the code."}]},{"_type":"block","markDefs":[],"style":"h4","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"That about wraps up my extensions setup for Visual Studio Code–which are your personal favorites? Before I go though, don’t forget that coding should be fun; you may need some tunes to help you along. The Spotify extension adds a tiny inline media player to the VS Code UI."}]},{"_type":"block","markDefs":[{"_key":"a9b2cbb2e2d6","_type":"link","href":"https://webdesign.tutsplus.com/a-guide-to-setting-up-vs-code-for-front-end-development--cms-30461t"}],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"Read the original article on "},{"_type":"span","marks":["em","a9b2cbb2e2d6"],"text":"Envato Tuts+"}]}]}
{"_type":"post","_createdAt":"2023-11-20T08:51:00.322+02:00","publishedAt":"2023-11-20T08:51:00.322+02:00","title":"How to Effectively Set Up NextJS with a Component Library using Monorepos","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A monorepo is a repository containing multiple related resources managed from an individual repository. Using NPM workspaces, we can build dynamic component libraries that numerous applications can consume, all retained in one version-controlled repository."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Monorepo's have been gaining traction over the years. Having worked with them for over a year in production environments, I can say one thing: "},{"_type":"span","marks":["em"],"text":"they beat git submodules"},{"_type":"span","marks":[],"text":"!"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A typical monorepo will contain multiple packages and projects that are closely related. We can work more efficiently using a monorepo in a way that allows us to move away from:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"initializing git submodules (a repo within a repo)"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"linking local packages from other locations on the disk that must be updated and installed."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"NPM supports "},{"_type":"span","marks":["code"],"text":"workspaces"},{"_type":"span","marks":[],"text":", which effectively allows for a monorepo structure. You'll need to use NPM v7 and above to set up a "},{"_type":"span","marks":["code"],"text":"workspace."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Recently, I embarked on building a suite of components for a NextJS project. I wanted to ensure that I could actively develop components (facilitated by Storybook) so that my NextJS projects could consume those components."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Historically, NextJS projects have always supported a "},{"_type":"span","marks":["code"],"text":"components"},{"_type":"span","marks":[],"text":" folder. However, NextJS restricts this to the application itself. I aim to treat my component library like any other 3rd-party library I'd want to import into my projects. An architecture of this manner is easily achievable using a mono repo."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Initialise Project Structure"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Our first step is to set up a project structure supporting a monorepo. Create a folder where your monorepo can live:"}]},{"_type":"code","code":"mkdir my-monorepo && cd my-monorepo ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Great. Our next step is to create a "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" file. We can bootstrap one quickly by running the following:"}]},{"_type":"code","code":"npm init -y ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"That should produce the following output:"}]},{"_type":"code","code":"{ \"name\": \"my-monorepo\", \"version\": \"1.0.0\", \"description\": \"\", \"main\": \"index.js\", \"scripts\": { \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\" }, \"keywords\": [], \"author\": \"\", \"license\": \"ISC\" } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's add some items to our "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" before installing dependencies. We'll need to add a "},{"_type":"span","marks":["code"],"text":"workspaces"},{"_type":"span","marks":[],"text":" key. Doing so informs NPM where our projects and packages sit."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"NPM comes with a nifty command for adding workspaces; run the below commands and follow the instruction wizard that each command outputs:"}]},{"_type":"code","code":"npm init -w ./packages/component-library npm init -w ./projects/monorepo-site ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Executing the above will run "},{"_type":"span","marks":["code"],"text":"npm install"},{"_type":"span","marks":[],"text":" for you as well. At this point, we will see our first fundamental differences between a traditional repo and a monorepo:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" exists in the root and within every \"workspace.\" You may notice that there is only one occurrence of "},{"_type":"span","marks":["code"],"text":"node_modules"},{"_type":"span","marks":[],"text":" and "},{"_type":"span","marks":["code"],"text":"package-lock.json."},{"_type":"span","marks":[],"text":" Now, all workspace dependencies get managed from the root folder."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Next up, we'll look into how to install workspace-specific dependencies."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Install dependencies"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When installing package or project dependencies, we can still leverage "},{"_type":"span","marks":["code"],"text":"npm install"},{"_type":"span","marks":[],"text":" - however, this needs to be run from the root folder and requires that we pass a "},{"_type":"span","marks":["code"],"text":"-w"},{"_type":"span","marks":[],"text":" flag to the command. Let's run through installing NextJS."}]},{"_type":"code","code":"npm install --save next react react-dom -w monorepo-site ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Run the command above from the "},{"_type":"span","marks":["code"],"text":"root"},{"_type":"span","marks":[],"text":" of your project folder, where "},{"_type":"span","marks":["code"],"text":"node_modules"},{"_type":"span","marks":[],"text":" and the "},{"_type":"span","marks":["code"],"text":"package-lock.json"},{"_type":"span","marks":[],"text":" can be found. The only thing to call out here is that the value passed to "},{"_type":"span","marks":["code"],"text":"-w"},{"_type":"span","marks":[],"text":" must match the "},{"_type":"span","marks":["code"],"text":"name"},{"_type":"span","marks":[],"text":" field of one of your "},{"_type":"span","marks":["code"],"text":"project"},{"_type":"span","marks":[],"text":" or "},{"_type":"span","marks":["code"],"text":"packages"},{"_type":"span","marks":[],"text":" package.json. In this example, the "},{"_type":"span","marks":["code"],"text":"name"},{"_type":"span","marks":[],"text":" field of "},{"_type":"span","marks":["code"],"text":"projects/monorepo-site/package.json"},{"_type":"span","marks":[],"text":" is "},{"_type":"span","marks":["code"],"text":"monorepo-site."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If all was successful, you should see the dependencies added to "},{"_type":"span","marks":["code"],"text":"projects/monorepo-site/package.json."}]},{"_type":"code","code":"\"dependencies\": { \"next\": \"^13.0.6\", \"react\": \"^18.2.0\", \"react-dom\": \"^18.2.0\" } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Repeat this process for any necessary dependencies in "},{"_type":"span","marks":["code"],"text":"packages/component-library"},{"_type":"span","marks":[],"text":" as well."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Setup Components and StoryBook"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"Please note that this is not a tutorial on how to get set up with Storybook."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's take some steps to get Storybook set up correctly. We'll also create a dummy component to verify that our setup works. As StoryBook isn't a dependency of either "},{"_type":"span","marks":["code"],"text":"packages"},{"_type":"span","marks":[],"text":" or "},{"_type":"span","marks":["code"],"text":"projects,"},{"_type":"span","marks":[],"text":" we can install it in the root of our monorepo:"}]},{"_type":"code","code":"npm install --save-dev @storybook/react @storybook/manager-webpack5 @storybook/builder-webpack5 @storybook/addon-postcss ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's also add the build commands to the root monorepo package.json:"}]},{"_type":"code","code":"\"storybook\": \"start-storybook -p 6006\", \"build-storybook\": \"build-storybook\" ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Next, we'll need to create the "},{"_type":"span","marks":["code"],"text":".storybook"},{"_type":"span","marks":[],"text":" folder; let's do this and add it to the root of the monorepo. You'll want to add two files to this folder:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"main.js"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"preview.js"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":" is where we configure StoryBook's settings:"}]},{"_type":"code","code":"module.exports = { \"stories\": [ \"../packages/component-library/**/*.stories.jsx\", ], \"addons\": [ { name: '@storybook/addon-postcss', options: { styleLoaderOptions: {}, cssLoaderOptions: { modules: true, sourceMap: true, importLoaders: 1, }, postcssLoaderOptions: { implementation: require('postcss'), }, }, } ], \"framework\": \"@storybook/react\", \"core\": { \"builder\": \"@storybook/builder-webpack5\" } } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In our "},{"_type":"span","marks":["code"],"text":"main.js"},{"_type":"span","marks":[],"text":" file above - we tell StoryBook where to look for component stories. We also include the "},{"_type":"span","marks":["code"],"text":"@storybook/addon-postcss"},{"_type":"span","marks":[],"text":" plugin that supports PostCSS. The extra options you see listed above enabled PostCSS Modules. The last two keys: "},{"_type":"span","marks":["code"],"text":"framework"},{"_type":"span","marks":[],"text":" and "},{"_type":"span","marks":["code"],"text":"core,"},{"_type":"span","marks":[],"text":" allow us to configure StoryBook to use Webpack v5."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In "},{"_type":"span","marks":["code"],"text":"preview.js,"},{"_type":"span","marks":[],"text":" add the following:"}]},{"_type":"code","code":"export const decorators = [ (Story, context) => { return ( <div style={{ padding: '1rem', display: 'flex', justifyContent: 'center' }}> <div> <Story {...context} /> </div> </div> ); } ]; ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The "},{"_type":"span","marks":["code"],"text":"preview.js"},{"_type":"span","marks":[],"text":" file allows us to customize the appearance of stories without affecting the component logic itself. Hence, the name \"decorator\". I've just added some basic styling here to center the button."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Next, we need to flesh out our "},{"_type":"span","marks":["code"],"text":"Button"},{"_type":"span","marks":[],"text":" component. In "},{"_type":"span","marks":["code"],"text":"package/component-library,"},{"_type":"span","marks":[],"text":" add a new folder called "},{"_type":"span","marks":["code"],"text":"Button"},{"_type":"span","marks":[],"text":"; in that folder, add the following files:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Button.module.css - handles the styling of the button"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Button.stories.jsx - handles the button story"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Button.jsx - the button component itself"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can find the contents of these files in the repo. Great, we need to see if we can import our component into NextJS."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Setup a NextJS project"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"We need to return to NextJS quickly to tie everything together meaningfully. First, add a "},{"_type":"span","marks":["code"],"text":"pages"},{"_type":"span","marks":[],"text":" folder to the NextJS site and the scripts needed in "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" to build NextJS."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To add the scripts, go to "},{"_type":"span","marks":["code"],"text":"projects/monorepo-site/package.json"},{"_type":"span","marks":[],"text":" and replace the current "},{"_type":"span","marks":["code"],"text":"scripts"},{"_type":"span","marks":[],"text":" field with:"}]},{"_type":"code","code":"\"scripts\": { \"dev\": \"next dev\", \"build\": \"next build\", \"start\": \"next start\", \"lint\": \"next lint\" } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the "},{"_type":"span","marks":["code"],"text":"pages"},{"_type":"span","marks":[],"text":" folder, add an "},{"_type":"span","marks":["code"],"text":"index.js"},{"_type":"span","marks":[],"text":" file with the following contents:"}]},{"_type":"code","code":"const Home = () => { return ( <main> <h1>Hello! This is a NextJS + StoryBook Monorepo</h1> </main> ) } export default Home ","language":"text"},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Add the Component to NextJS"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Now, it's time to import our component into NextJS. Let's import the button into "},{"_type":"span","marks":["code"],"text":"projects/monorepo-site/pages/index.js"},{"_type":"span","marks":[],"text":":"}]},{"_type":"code","code":"import Button from '../../../packages/component-library/Button/Button.jsx' ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When you run "},{"_type":"span","marks":["code"],"text":"npm run dev"},{"_type":"span","marks":[],"text":", you'll likely run into an error. NextJS does not transpile ES6 JavaScript from "},{"_type":"span","marks":["code"],"text":"node_modules"},{"_type":"span","marks":[],"text":" or any other local folder outside its reach."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To get around this problem, configure a property in "},{"_type":"span","marks":["code"],"text":"next.config.js"},{"_type":"span","marks":[],"text":" called "},{"_type":"span","marks":["code"],"text":"transpileModules"},{"_type":"span","marks":[],"text":":"}]},{"_type":"code","code":"export const nextConfig = { transpileModules: ['component-library'] } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Note that the "},{"_type":"span","marks":["code"],"text":"workspace"},{"_type":"span","marks":[],"text":" name, "},{"_type":"span","marks":["code"],"text":"component-library,"},{"_type":"span","marks":[],"text":" is passed, not the path to the folder on the disk. You'll need to restart your dev server."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Your custom Button component now lives in NextJS and StoryBook and can be iterated upon and tracked in the repo!"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"You may run into trouble with NextJS asking for React v18 if you use NextJS 13. This tutorial was written using NextJS v12 and React v17.0.2"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Additional Scripts for Convenience"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To run our build commands, we'd have to "},{"_type":"span","marks":["code"],"text":"cd"},{"_type":"span","marks":[],"text":" into every "},{"_type":"span","marks":["code"],"text":"project"},{"_type":"span","marks":[],"text":" or "},{"_type":"span","marks":["code"],"text":"package"},{"_type":"span","marks":[],"text":". Doing so can be time-consuming, so let's add the following "},{"_type":"span","marks":["code"],"text":"scripts"},{"_type":"span","marks":[],"text":" to the "},{"_type":"span","marks":["code"],"text":"package.json"},{"_type":"span","marks":[],"text":" found in the root:"}]},{"_type":"code","code":"\"scripts\": { \"build\": \"npm run build --workspaces --if-present\", \"dev:monorepo-site\": \"npm run dev -w monorepo-site\", } ","language":"text"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's run through these two commands as they differ slightly. Running "},{"_type":"span","marks":["code"],"text":"npm run build"},{"_type":"span","marks":[],"text":" in the root will allow us to fire off individual "},{"_type":"span","marks":["code"],"text":"build"},{"_type":"span","marks":[],"text":" scripts in all workspaces. If the "},{"_type":"span","marks":["code"],"text":"build"},{"_type":"span","marks":[],"text":" task isn't in a "},{"_type":"span","marks":["code"],"text":"package"},{"_type":"span","marks":[],"text":" or "},{"_type":"span","marks":["code"],"text":"project"},{"_type":"span","marks":[],"text":" package.json, it will simply skip it."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If, at some point, this particular example scaled to have three production sites, all with a "},{"_type":"span","marks":["code"],"text":"build"},{"_type":"span","marks":[],"text":" command, we could run "},{"_type":"span","marks":["code"],"text":"npm run build"},{"_type":"span","marks":[],"text":" from the root, and for each workspace, the build task will execute. Pretty nifty!"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["code"],"text":"npm run dev:monorepo-site"},{"_type":"span","marks":[],"text":" is slightly different - this command targets one workspace and fires off the "},{"_type":"span","marks":["code"],"text":"script"},{"_type":"span","marks":[],"text":" defined within that workspace (as long as it exists). Doing so allows us to spin up the "},{"_type":"span","marks":["code"],"text":"dev"},{"_type":"span","marks":[],"text":" server for "},{"_type":"span","marks":["code"],"text":"monorepo-site"},{"_type":"span","marks":[],"text":" from the root without navigating to the folder."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's review where we're at:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"We initialized a monorepo structure using "},{"_type":"span","marks":["code"],"text":"workspaces"},{"_type":"span","marks":[],"text":" in NPM"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Set up a component library and production site with StoryBook and NextJS."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Added scripts that will make our developer experience more accessible to manage."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"All our dependencies get managed from the top level, and we do not have to pull in other repos or git submodules."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"At this point, we could easily add a new site project or extend our component library. We could also add other "},{"_type":"span","marks":["code"],"text":"packages"},{"_type":"span","marks":[],"text":" that may deal with custom server functionality, CMS integrations, or API support. The possibilities are endless. Now it's time for you to try!"}]}]}
{"_type":"post","_createdAt":"2023-01-09T07:35:07.322+02:00","publishedAt":"2023-01-09T07:35:07.322+02:00","title":"How To Motivate and Inspire Individual Contributors","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Individual Contributors play an essential role within a development team and organisation. Engineering managers are in charge of leading and managing Individual Contributors to successful outcomes while ensuring workplace happiness. More importantly, as most engineering managers were once Individual Contributors, they are uniquely positioned to relate and empathise with the individuals working in their team."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As each contributor brings their personality to the table, engineering managers sometimes need to adjust their approach to motivate and inspire them when workload increases or pressures build up."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This article will explore five strategies engineering managers can leverage to inspire and motivate Individual Contributors."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Setting Clear Goals and Expectations"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Individual Contributors must know what is expected of them. This helps people feel secure and gives them a purpose within the organisation. Once expectations are set, individual contributors can begin to understand their roles and determine how they can contribute to the entire team's success."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Using SMART goals: Specific, Measurable, Achievable, Relevant, and Time-bound can help guide Individual Contributors to deliver real value on time."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"While project goals are important, personal goals are equally important. You must always be conscious as an engineering leader that individual contributors have ambitions to learn and master technologies that may or may not be related to the current stack they’re working on."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's worthwhile setting yearly personal goals so that they feel like they are working towards progressing their career objectives, not just the company's business goals."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Providing Ongoing Feedback and Support"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Continuous and positive feedback helps individual contributors thrive in their position."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Feedback can be given daily, weekly or monthly through several activities such as:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Async check-ins on Slack"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"1:1 Meetings"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Quarterly Goal Realignments"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Creating a solid rapport with Individual Contributors can help make this process smoother. Ongoing support can help Individual Contributors better understand their strengths and weaknesses and allow engineering managers to step into mentorship positions when and if needed."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The most important part of this process is keeping your word as a manager. Don’t commit to helping Individual Contributors meet their goals if six months go by and you haven’t spoken about it once. A level of trust and dependency should be held in the utmost regard. When things get tough, Engineering Managers must remain calm and guide Individual Contributors to the best possible outcomes."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Furthermore, sometimes Individual Contributors have bad days. Support, in this case, may have nothing to do with work and business goals. It may require you to show more empathy and understanding than usual."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Encouraging and Supporting Career Development"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It should not be taken for granted that Individual Contributors’ have their own career goals. These goals may often not, or potentially never, align fully with the purposes of the company they work for. Engineering managers must respect that. Showing genuine interest in an individual's career development can naturally motivate them."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Company policies can go a long way to aiding engineering managers in such goals:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Professional Development budgets"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Paid / Sponsored conferences and trips"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Encouraging up-skilling in fields that benefit the individual and the company"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Assigning them responsibility on projects that will help them develop their skills."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Whereas having conversations about career trajectories outside the current company may feel awkward and inappropriate, Individual Contributors will likely move on at some point in the future. Their position under your supervision will provide them with skills and experience that will encourage them later in life, so you should treat any Individual Contributor in a way that their tenure with you contributes positively to their career."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Recognising and Rewarding Contributions"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Recognising and rewarding an individual's hard work can increase morale and motivation. Recognising is more important than rewarding. Public recognition in front of peers or non-technical stakeholders of the company can go a long way. It should be noted that recognition applies to more than just great code:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"great teamwork,"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"going above and beyond,"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"and making a concerted effort to improve, can all be recognised in a meaningful way."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Hard work and that extra 10% should always be respectfully valued as it highlights Individual Contributors who are passionate about their roles and responsibilities."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Fostering a Positive and Inclusive Work Environment"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Attempt to keep dialogues and hard conversations positive and inclusive. Transparent communication across teams and hierarchies can ensure individual contributors feel respected and included in everyday business and technical decisions. It brings team members closer together and breaks down cultural and personal barriers."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Encourage an environment of making mistakes that can be learned from and address team feedback as often as possible. Perhaps most importantly, if you, as an engineering manager, can establish a well-knit and robust community within your engineering team, it will encourage Individual Contributors to work hard and want to work."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"These are the beginnings of great, grass-root company culture - something which is highly sort after in the tech industry and often separates good companies from great companies."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"By ensuring Individual Contributors are inspired and motivated to come to work with a positive attitude and a willingness to work hard, the entire business can experience successful delivery of value. Setting SMART business and personal goals for Individual Contributors can help them navigate what's expected of them. Providing ongoing feedback and support can help them feel secure and that someone is on their side if they encounter a challenge."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Furthermore, creating a positive work environment and celebrating success can help Individual Contributors feel recognised and valued."}]}]}
{"_type":"post","_createdAt":"2023-01-19T07:35:07.322+02:00","publishedAt":"2023-01-19T07:35:07.322+02:00","title":"Leveraging CommitLint for Consistent Commit Messages","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"CommitLint is a handy package that allows engineers to lint and control how Git commit messages are added to project changes. During the lifespan of development cycles, software engineers tend to get a bit sloppy when writing informative and precise commit messages. CommitLint intends to solve that by enforcing a particular style guide for commits."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This aids in standardising project commits across a multi-disciplinary team."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"Why Use CommitLint?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"There are several reasons to consider implementing CommitLint into your workflow:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Enforce a commit message style guide: This provides clarity to team members, keeps git logs and Git commit history consistent and predictable, and makes it easier to generate CHANGELOG's and release notes."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Ensures that commit messages contain the information that they should. This convention makes it easier for team members unfamiliar with, or close to the work to understand. At times it can be helpful to ensure that JIRA ticket numbers are placed within commits to allow engineers and other contributors to ensure that commit messages are descriptive and informative: This makes it easier for team members to understand the changes made and the reasoning behind them. It also makes it easier for other developers to understand the project's history when they join the team or when they need to make changes to the code."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Prevent mistakes and improve overall quality: CommitLint can prevent inappropriate or offensive language in commit messages and ensure that commit messages are written in English (or whatever language is specified)."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"How to Implement CommitLint?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To integrate CommitLint into a project, you must install it using "},{"_type":"span","marks":["code"],"text":"npm"},{"_type":"span","marks":[],"text":". Open a terminal window and navigate to the root directory of your project. Then, run the following command:"}]},{"_type":"code","code":" npm install commitlint --save-dev ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Next, you will need to create a configuration file for CommitLint. This file will specify the rules that CommitLint should enforce in your project."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To make the configuration file, run the following command:"}]},{"_type":"code","code":" npx commitlint --init ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The above will create a "},{"_type":"span","marks":["code"],"text":"commitlint.config.js"},{"_type":"span","marks":[],"text":" file in the root directory of your project. You can edit this file to specify the rules you want CommitLint to enforce."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One extra step is required if you want to run CommitLint on commit instead of manually."}]},{"_type":"block","markDefs":[{"_key":"f7dd8193dd94","_type":"link","href":"https://typicode.github.io/husky/#/"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"You'll need first to install "},{"_type":"span","marks":["f7dd8193dd94"],"text":"Husky"},{"_type":"span","marks":[],"text":":"}]},{"_type":"code","code":" npm install husky --save-dev ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Then run the below to activate Husky Hooks:"}]},{"_type":"code","code":" npx husky install ","language":"bash"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Finally, you'll need to add the commitlint hook to Husky:"}]},{"_type":"code","code":" npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}' ","language":"bash"},{"_type":"block","markDefs":[{"_key":"8c59cf454c1f","_type":"link","href":"https://commitlint.js.org/#/guides-local-setup"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can find more information on the above steps in the "},{"_type":"span","marks":["8c59cf454c1f"],"text":"local setup guide"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"CommitLint Configuration Options"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The "},{"_type":"span","marks":["code"],"text":"commitlint.config.js"},{"_type":"span","marks":[],"text":" file allows you to specify a wide range of configuration options for CommitLint. Some of the most important options include:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"Rules"},{"_type":"span","marks":[],"text":": An object that sets the rules that CommitLint should enforce. Each key in the object corresponds to a specific rule, and the value is either true or false."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"parserPreset"},{"_type":"span","marks":[],"text":": The parser preset that should be used to parse commit messages."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["strong"],"text":"formatter"},{"_type":"span","marks":[],"text":": The formatter that lints the output of results from CommitLint."}]},{"_type":"block","markDefs":[{"_key":"bc6e73855470","_type":"link","href":"https://commitlint.js.org/#/reference-configuration"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"For a complete list of configuration options, see the "},{"_type":"span","marks":["bc6e73855470"],"text":"CommitLint documentation"},{"_type":"span","marks":[],"text":"."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"How to Integrate CommitLint into Your CI Pipeline"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To integrate CommitLint into your project's CI pipeline, you must add it as a step in the pipeline configuration. For example, if you are using Travis CI, you can add the following to your .travis.yml file:"}]},{"_type":"code","code":"script: - npm run lint: commit ","language":"yaml"},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The above will cause CommitLint to run on every commit, ensuring that all commit messages in your project follow the specified convention."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":["strong"],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In summary, CommitLint is a helpful tool that can enforce a consistent commit message style, ensure that commit messages are descriptive and informative and improve the overall project quality."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"By using CommitLint, you can make it easier for team members to understand the history of the project and its changes, allowing you to improve the professionalism and clarity of your project's commit history."}]}]}
{"_type":"post","_createdAt":"2023-02-21T07:35:07.322+02:00","publishedAt":"2023-02-21T07:35:07.322+02:00","title":"10 Proven Techniques for More Effective Code Reviews","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Any engineer will tell you performing code reviews is as normal as breathing air in the tech world. Code reviews are integral to the software development lifecycle by ensuring quality, standards, team collaboration, knowledge sharing and learning."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As much as we know that code reviews are mandatory, the jury is out about how best to dish them out. A code review, in many ways, becomes an intersection of human and machine collaboration. Humans debate and collaborate to better the machine code to ensure its intended function."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's this very intersection where the problem begins. Humans have a hard enough time conversing with each other, never mind talking about code that a machine is barely intelligent enough to process. Often, code reviews can become personal, offensive and filled with dread. They can hold up sprints or project deadlines due to stubborn and egotistical mindsets or cause panic in those who don't understand the intended feedback."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Fortunately, there are ways to overcome these challenges, so here are ten techniques for giving, accepting and working with code reviews:"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Perform Multiple Reviews Per Feature"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One giant misconception about code reviews is that the code should only be reviewed once. It's like arriving at your destination without knowing how you got there. Whereas it's not feasible or efficient to constantly check the same code, it is valuable to be close to the development of that feature on a daily or weekly basis."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This helps you, as the reviewer, understand what the engineer submitting the code review is trying to achieve; it outlines their thought process and uncovers their struggles."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I would suggest regular check-ins regarding code reviews, perhaps when a feature is 25% or 75% complete. This gives you multiple opportunities to course correct if need be. Junior developers will also appreciate the guidance, as it will feel like you're building the feature with them."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As time may not always allow for three rounds of detailed review, doing a brief check-in via a Draft PR for the 25% and 75% milestones may be worthwhile. A thorough review can be performed when the submitting engineer thinks the feature is finished."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Define a Clear Process"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Before launching into feature development, defining a straightforward process for code reviews can go a long way. This may involve:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Who will perform code reviews?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"What automated tools will be used?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"How often should a code review take place?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"What activities will take place during the code review?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"What are the steps to submit a code review?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Who is responsible for assigning tickets/code reviews to relevant engineers?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"How should developers prepare their branches?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"What merge strategy should be used?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Should branches be deleted after merging?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It often helps to define a document that outlines these questions and their answers to help make the team more efficient. Once all members agree on a process, follow it and try not to change it too much."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Always communicate with the team when there's a process change and ensure complete alignment and understanding."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Focus On Code, Not People"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"All too often, code reviewers can leave borderline insulting, offensive or rude comments on PRs - written language can be conveyed and interpreted in many different ways, depending on language barriers, culture, and the general mood/sensitivity of the engineer submitting their work."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Feedback like this can often come back as \"too direct\" and does not contribute to a cohesive working environment. When addressing feedback on a code review, try to:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Keep it light-hearted, kid around a bit, or use emojis."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Be empathetic."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Suggest, rather than instruct: \"Perhaps it would be better to…\" rather than \"You must stop doing….\""}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Avoid blameful or aggravating language: \"A better understanding of the CSS cascade would help make this code more efficient\" instead of \"You don't understand how the CSS cascade works\"."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Treat every feedback item as a learning experience for the engineer having their work reviewed. Likewise, as the code reviewer, every feedback item you leave is a teaching moment."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Ask Questions; Dont Assume Mistakes"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One thing I've found particularly useful in the past is asking a question instead of calling out a mistake. This disarms the situation immediately as your intention is clear; asking a question is less aggressive than pointing out that the code submitted needs refactoring. Asking a question can take many forms:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"\"Can you explain to me how this function works?\""}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"\"Why is this value a String on line 117 but an Array on 120?\""}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"\"What is the purpose of this variable?\""}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Asking questions disarms the engineer who submitted the request and helps you understand the logic better. The reviewee will feel like they are teaching you, increasing their self-confidence."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Leverage Automated Tooling"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Perhaps the most critical point on this list is that as a code reviewer, you do not want to spend time calling out code mistakes that could have been caught with linting tools…especially if the project has them set up already."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Many platforms offer integrations for automated tooling to run in CI/CD pipelines that can make this a breeze. Github, for instance, allows you to run automated tasks through GitHub Actions, which can fire on pull requests or git push."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You can also use tools like Husky or Lint-Staged to run linting scripts on only the code that has changed on the branch. This way, code styling, best practices and silly errors can be caught without needing a human eye."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Of course, automated tools can't catch everything, but I would consider them for the following:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"CSS Code Styles"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"JavaScript Code Styles"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Framework Best Practices"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Unit Testing"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Performance / e2e Testing"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Encourage Open Communication and Dialogue"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The hierarchical nature of code reviews (reviewer vs reviewee) can inherently stall open communication. The code review format isn't a comfortable platform for clear communication, either. Whereas issues and problems can be easily pinpointed and called out in code, the details may need to be taken offline."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"While having these conversations, it's essential to gauge the following:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"How much time you're spending mentoring, teaching and explaining the issues found in the PR?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The openness of the reviewee. They also have to take the code review process as a learning experience and not get too conscious of the feedback provided."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Actively encourage the challenging of opinion in a way that keeps open dialogue consistent. Less senior team members need to be encouraged to challenge opinions respectfully. This can be done in a 1:1 session or as a team feedback session if the reviewee feels comfortable."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Do More Than Review"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Believe it or not, there is more to code reviews than just reviewing code. A thorough code review means that, as a senior engineer, you take a vested interest in the submitted code. This may include:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Pulling the code down locally and running a production build."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Testing the feature in the browser, validating the technical approach and ensuring it meets product stakeholder expectations."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Performing some light but meaningful QA on the feature."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Suggesting improvements and providing options for optimising."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Thinking ahead and determining if this feature could have a domino effect across the project."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Be aware of these extra responsibilities. As the reviewing engineer, you give the thumbs up for when this code is merged."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Use Code Suggestions and Examples"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Often, our understanding of feedback can differ from engineer to engineer. This is mainly due to experience; some senior engineers see things more clearly than others."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In the past, I've received feedback that I haven't entirely understood or that's made me hopelessly lost. Sometimes the feedback required a subtle refactor or update to logic I did not completely understand."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If a reviewing engineer had provided a clear example of what they wanted to see, I could have been far more efficient in my duties. GitHub has a great feature called Suggested Changes, allowing reviewers to suggest code updates in line with commented feedback."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Linking to related examples, educational reading, articles or blog posts can also help achieve positive and sought-after results from reviewees in a way that teaches them and allows them to improve."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Don't Sweat The Small Stuff"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Sometimes, less experienced engineers get stuck on the things that matter the least. Whether that is tabs vs spaces or how to name variables or functions. Some things don't matter as much as others think they do."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As a senior engineer on the project, you have the authority to override this. However, it does become a case of \"choosing your battles\". It's not efficient nor practical to have back and forth on how a constant has been named."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You need to learn to let certain things go."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This can be dependent on the technologies or frameworks used. As a general rule of thumb, always prioritise the issues that could cause the most damage in the long run. Naming conventions and other callouts can be tackled later if they do not directly affect the functionality or cause code styles to erode on lint."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Implement a Code Review Check List"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A code review checklist is an essential and effective way of ensuring consistency within each PR. Nowadays, many manual tasks are mainly automated through tests, so you know that a particular task succeeds or fails when a PR is created."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For other more abstract items, though, a checklist in the PR description can go a long way. You may want to include the following:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"A link to the task ticket."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"A description of the technical approach or solution."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Links to designs."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Screenshots of the feature."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Instructions on how to test."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"A section to confirm functionality, address doubts etc."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If every engineer takes the time to fill in this section consistently, it provides a meaningful database of progress over time. This can be useful when uncovering where a bug originated. In GitHub, you can use PR Templates to implement a pre-defined setup when a PR is created."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Ensuring more effective code reviews can be a difficult learning curve for senior and junior engineers. Once mastered, engineers can change the direction and output of a team by following simple and tested communication techniques."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Remember to communicate openly, provide direct feedback on the code, not the person, perform thorough reviews beyond \"just reviewing code\", and leverage automated tooling to save you from searching for remedial mistakes."}]}]}
{"_type":"post","_createdAt":"2016-05-30T07:35:07.322+02:00","publishedAt":"2016-05-30T07:35:07.322+02:00","title":"State of the Web Industry in South Africa","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"For the next few minutes, I’m going to give you a peek into the country I call home; its culture, the web industry, and some of our unique and interesting challenges when it comes to the internet."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa tends not to get too much attention when it comes to the web industry, nevertheless we have an exploding culture of talented developers, startups, industry and ideas, and I am quite honoured to share it with all of you."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Before we continue, it should be stated that South Africa has been a hot topic in local and international news lately, for many reasons. I will be making generalisations from time to time for the sake of brevity–any point I make should please be taken with a pinch of neutrality."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Sit back, grab a cup of coffee and relax. Our journey is about to begin."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"South Africa: Land of Opposites"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa, to some South Africans and the media, is often referred to as the “Land of Opposites”, or “Land of Contrasts”. It’s a fair statement to make. Due to a rocky past, the country has been left with some very visible contradictions and problems. It’s quite common to see incredible wealth right next to saddening poverty. South Africa struggles with inequality and corruption, like many other countries."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It’s hard to place this nation as a First or Third World country, as we sit somewhere in the middle. We’re one of the few countries to have three capital cities: Cape Town, Johannesburg, and Bloemfontein. Each city is a power house for different parts of government and industry. Cape Town and Johannesburg are by far the most built up and industry-centric areas to live and work in."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Cape Town has the mountains, the wine farms and the ocean on almost every side, while Johannesburg is a sprawling metropolis, a concrete jungle. We’re also one of the only countries which has over ten official languages. That’s right, ten! Few individuals speak all of them, but the more well known ones include: English, Afrikaans, and Xhosa."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"These three are the main languages you are taught in school. Afrikaans is derivative of Dutch, brought here by the settlers in the 1600’s. Xhosa, is a tribal language spoken by many Black Africans in the southern, coastal regions of South Africa. It’s important to understand that for the most part, English and Afrikaans communities are more “westernised” in terms of their cultures, family dynamics, and social behaviour, where as Xhosa communities tend to be more traditional, supportive, and community-based."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"So, why am I telling you all of this? Well, it’s important to understand the background of South Africa’s population in order to appreciate how information is accessed and assimilated nowadays. Socioeconomics play a very big role, and in fact, more of a role than any experienced web developer may even consider. Take a look at the statistics below regarding demographics here. We’ll come back to these figures later:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Population\t0-14 years old\t15-64 years old\t64+ years old 53 million\t29%\t66%\t5.5% These numbers give you an idea of how much of the population has been born and raised with the internet."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The next statistical point provides evidence to an overwhelming challenge that we have in South Africa regarding internet access:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Ethnicity\t(%) of Population\t(%) Distribution of Wealth"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Black African\t79.2\t43.4"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"White\t8.9\t41.5"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Coloured\t8.9\t8.0"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Asian / Indian\t2.5\t6.2"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"You may be asking yourself; if Black Africans make up almost 80% of the population, and White Africans a mere 9%, how is it that White Africans share almost the same distribution of income and wealth as the majority of the country? I won’t get into the “why’s” as it doesn’t really lend itself to the point of this article, but be aware that this statistical fact plays a massive role in the question of internet access and web-related services in South Africa."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Web Culture in South Africa"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Now that you have something of a “big picture” idea of South Africa, let’s get down to the entire point of this article: the web."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In more built up areas where the urban mindset dominates, so does technology. We are nowhere near as advanced as the U.S. or certain European countries in Europe when it comes to Internet access or major web-based corporations."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa’s information access to the World Wide Web is essentially provided by a few main companies, most well known of all being the telecommunications giant: Telkom."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Telkom runs the phone lines, the ADSL lines, and the vast majority of ADSL based internet access. The majority of alternative ISPs backbone off Telkom, or cellphone giants like Vodacom and MTN. Over the last few years, telecommunication companies and certain ISPs have been making big progress on fibre-based internet, in partnership with the government, who have spent some time digging up pavements in urban and suburban areas to lay the cable. In fact, a few (albeit more affluent) areas and ISPs are already offering fibre-based internet which is a huge win for us as a country."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa is bursting at the seams when it comes to web-related skills. Whether you’re a UX Designer, Front-end Developer, Full Stack, or DevOps, there’s opportunity for you here."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Industry"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The web industry in Cape Town is exploding. More and more companies are letting go of print and beginning to invest in web. Even agency-based businesses like Ogilvy, TBWA, and Young & Rubicon are beginning to embrace web-based technologies and mindsets not only when building websites, but also for engaging with target markets."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Unfortunately, the spike in internet-based careers has been something of a recent occurrence. To say that we are a bit “delayed” in pushing experienced individuals into the workforce is a concern, however it is slowly gaining momentum."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Over the last few years many international and national companies have started to set up shop in urban centres. Amazon, Google, and others have established offices and support centres with regards to aiding the African timezone and countries. Office space, on a square metre basis, depending on where you are located within the city centres, is a lot more affordable than setting up premises in New York or London. In fact, some of our own web based giants like TakeaLot and Kalahari.net have settled into towering branded buildings in Cape Town, standing as a stark reminder of their success in the internet business, especially in South Africa."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Careers"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I’d like to start this section off with the following quote from CareerJunction’s, Odile Badenhorst:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"“…software development is undoubtedly the most sought-after IT skill set so far for 2016. On CareerJunction, every second vacancy posted within the IT sector is for Software Developers.” According to research from CareerJunction, programmers with C# skills are the most sought after in the South African job industry, shortly followed by systems and network administrators."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Even more exciting is that the list of skills below (some of the most sought after skills in South Africa) trends closely to international standards:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"(UX/UI) designers and developers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Full-stack Web and product developers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Network engineers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Security and Cyber-Security professionals"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Mobile engineers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"IT project managers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Cloud architects and integration"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Data scientists"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Content management system skills"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"It may sound like a bit of contradiction when I stated above that:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"“South Africa tends not to get too much attention when it comes to the web industry”. In the same sentence, I’ll tell you that it’s probably one of the most exciting places to begin or further your career. There is a great demand for web-based professionals here. From Ruby and Python, to Javascript and Node.js, even SysAdmin’s and DevOps - job boards are flush with positions on just about every level of experience. It’s an incredibly exciting time to be in the web industry."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In fact, Career Junction voted IT related careers as one of the top, most well-paying careers in South Africa in 2015."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The South African web industry has many great opportunities for young, inexperienced developers to learn. More experienced developers are always happy to help, to explain and provide a nurturing, yet challenging environment for young developers. This makes getting the nod for web-based jobs relatively easy in terms of what is expected of you. However, the curve tends to get aggressively steeper when you look at mid to senior positions in industry, due to the lack of experienced individuals within these positions."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Standard of Living in Web Industry"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Our currency is weak when compared to the USD or GBP, so from an international perspective salaries may look below par, but it is a lot cheaper to live here depending on your financial situation. Please see the table below (at time of publication, 1 South African Rand equals 0.063 $USD):"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Job Function"},{"_type":"span","marks":[],"text":"20 - 30 y/o"},{"_type":"span","marks":[],"text":"30 - 40 y/o"},{"_type":"span","marks":[],"text":"40 - 50 y/o"},{"_type":"span","marks":[],"text":"50 - 60 y/o"},{"_type":"span","marks":[],"text":"Designer"},{"_type":"span","marks":[],"text":"R13k- R23k"},{"_type":"span","marks":[],"text":"R27k- R33k"},{"_type":"span","marks":[],"text":"R27k- R33k"},{"_type":"span","marks":[],"text":"Systems Admin"},{"_type":"span","marks":[],"text":"R15k-R22k"},{"_type":"span","marks":[],"text":"R23k - R33k"},{"_type":"span","marks":[],"text":"R33k - R44k"},{"_type":"span","marks":[],"text":"R44k - R46k"},{"_type":"span","marks":[],"text":"Network Engineer"},{"_type":"span","marks":[],"text":"R15k-R22k"},{"_type":"span","marks":[],"text":"R22k-R32k"},{"_type":"span","marks":[],"text":"R32k-R45k"},{"_type":"span","marks":[],"text":"Database Engineer"},{"_type":"span","marks":[],"text":"R16k-R31k"},{"_type":"span","marks":[],"text":"R42k-R47k"},{"_type":"span","marks":[],"text":"R47k-R49k"},{"_type":"span","marks":[],"text":"R49k-R53k"},{"_type":"span","marks":[],"text":"Mobile App Developer"},{"_type":"span","marks":[],"text":"R20k-R30k"},{"_type":"span","marks":[],"text":"R30k-R40k"},{"_type":"span","marks":[],"text":"R40k-R60k"},{"_type":"span","marks":[],"text":"Security Specialist"},{"_type":"span","marks":[],"text":"R20k-R33k"},{"_type":"span","marks":[],"text":"R33k-R43k"},{"_type":"span","marks":[],"text":"R43k-R52k"},{"_type":"span","marks":[],"text":"R52k-R60k"},{"_type":"span","marks":[],"text":"Web Developer"},{"_type":"span","marks":[],"text":"R20k-R31k"},{"_type":"span","marks":[],"text":"R39k-R41k"},{"_type":"span","marks":[],"text":"R41k-R46k"},{"_type":"span","marks":[],"text":"Software Developer"},{"_type":"span","marks":[],"text":"R22k- R37k"},{"_type":"span","marks":[],"text":"R45k-R53k"},{"_type":"span","marks":[],"text":"R50k-R55k"},{"_type":"span","marks":[],"text":"Project Manager"},{"_type":"span","marks":[],"text":"R25k-R34k"},{"_type":"span","marks":[],"text":"R34k-R40k"},{"_type":"span","marks":[],"text":"R40k-R52k"},{"_type":"span","marks":[],"text":"Executive"},{"_type":"span","marks":[],"text":"R65k-R74k"},{"_type":"span","marks":[],"text":"R74k-R78k"},{"_type":"span","marks":[],"text":"R78k-R93k"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Opportunities"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To further my point, I’d like to present you with the list of skills required for Critical Skill’s Visa in South Africa:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"CISCO Solution Specialist and CISCO Engineer"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Solutions Architects in Telecommunications and ICT"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Integrated Developers (PHP, PERL, JAVA)"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Network Analyst"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"IT Security Specialist"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"System Integration Specialist"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Enterprise Architects"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Data Centre Operations"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Microsoft System Engineers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Network Controllers"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"AV Specialist (Anti-virus)"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":[],"text":"Desktop Support Engineer"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Interesting isn’t it? I’d like to make special note of “Integrated Developers”. PHP is a really sought after skill set in South Africa. It’s often overlooked when lined up next to Ruby and Python, but many smaller agencies focus primarily on WordPress, CodeIgniter and Laravel."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Startup Culture"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Startup culture is another section of industry which is bursting at the seams in South Africa. Not only is our environment and day to day life conducive to startup culture itself, but startup costs are also low. In major cities around the world, “hot-desking” has become very popular, and more internet-based startups are forgoing offices with high overheads for the “freelance” lifestyle."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let’s look at a very general example of costs involved in starting up a simple WordPress-based business."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Item"},{"_type":"span","marks":[],"text":"Cost"},{"_type":"span","marks":[],"text":"Monthly"},{"_type":"span","marks":[],"text":"Hourly"},{"_type":"span","marks":[],"text":"Includes"},{"_type":"span","marks":[],"text":"Hot Desk"},{"_type":"span","marks":[],"text":"R3,000"},{"_type":"span","marks":[],"text":"Yes"},{"_type":"span","marks":[],"text":"Wifi, Printer, Phone, Boardroom"},{"_type":"span","marks":[],"text":"Hosting / Server"},{"_type":"span","marks":[],"text":"R39"},{"_type":"span","marks":[],"text":"Yes"},{"_type":"span","marks":[],"text":"Server, DB, 3GB BW, 3GB HDD"},{"_type":"span","marks":[],"text":"Designers"},{"_type":"span","marks":[],"text":"R250 - R500"},{"_type":"span","marks":[],"text":"Yes"},{"_type":"span","marks":[],"text":"Graphic / Visual Design Services"},{"_type":"span","marks":[],"text":"Web Developers"},{"_type":"span","marks":[],"text":"R550-900"},{"_type":"span","marks":[],"text":"Yes"},{"_type":"span","marks":[],"text":"Front-end / Backend Development"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It would be irresponsible of me not to mention one of South Africa’s biggest internet-based exports and the first South African in space, Mark Shuttleworth. Shuttleworth’s company: Thawte Consulting specialised in digital certificates and internet security. In December 1999, Thawte Consulting was acquired by VeriSign for a whopping R3.5 Billion."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Web Education"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Unfortunately, one of South Africa’s greater pitfalls is education in web-based technologies. This is largely due to the fact that our internet and connectivity in more rural areas is limited. Aside from that, web orientated professionals are for the most part, self-taught."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Many of you reading this article may relate to the self-taught aspect of web. I am self-taught in just about everything I know about building websites. Our struggle here lies with the fact that the Department of Education doesn’t fully acknowledge web-related skills as being a credible degree to study. We have many great computer science degrees available at reputable institutions, but these focus more on C+ or C# with a focus on systems and security, or database engineering."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Coding Bootcamps aren’t present here either. Ironically, we do have one very interesting bootcamp which doesn’t cater to South African prospects at all; iXperience is solely for American IVY college students looking to get into tech and build standards compliant web apps. It’s run by South Africans, and is a great program for young foreigners to both learn code and get to know Cape Town."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"eCommerce"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"eCommerce in South Africa has gotten off to a slow start. The eCommerce industry accounts for $USD1.5 trillion in global sales, of which just R6-7 Billion is contributed by South Africa. Statistically, eCommerce accounts for 1% of the country’s retail sector, and is a sector which grows by about 25% per annum."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africans in general have always been slow in the uptake of online and mobile payments. There is a definitive notion of distrust when it comes to using a credit card online. MEF’s 2015 Consumer survey revealed that trust is the largest obstacle to growth in the mobile content and commerce industry here. Banks have been slow to the uprise in providing online banking technologies and even promoting safe, secure online transactions. Those that forged ahead, made it a tedious and frustrating experience."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The general consumer base tends to feel more trusting of eCommerce providers who sell more commodity-based items. Websites like takealot.com and bidorbuy.com are well respected and used often, helped by the many payment options and good return policies they offer."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Age also seems to be a contributing factor in eCommerce. Ageing generations steer away from it for the most part."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In conclusion, the biggest point that can be made about South African eCommerce is that there is major room for growth and opportunity."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"WordPress"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The WordPress community in South Africa is a small, but passionate one."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"httpswwwwoothemescomabout WooThemes Team WooThemes, the creators of WooCommerce, is based in Cape Town, South Africa and we’re incredibly proud to have such a giant in the WordPress community call South Africa home. Not only does WooThemes develop great software, they also host the annual Wordcamp Cape Town, as well as regular WordPress Meetups. A huge thanks is due to Hugh Lashbrooke, who is part of the WooThemes team, for all that he does in Cape Town for WordPress culture and the effort that goes into organising these events."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If you’re based in South Africa and are interested in joining the WordPress community, you can join the WordPress Cape Town slack channel."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"User Mindsets, Platforms, and Browser Stats Ok, some more numbers for you: Chrome currently holds 52% of the browser market globally. Internet Explorer and Firefox run quite far behind at 15%. In South Africa the statistics are more or less aligned. 55% of the market currently uses Google Chrome, with 22% using Internet Explorer and just ~12% using Firefox."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"According to statcounter.com almost 50% of South African mobile and tablet consumers use Opera as their mobile browser of choice. Chrome is second to this with ~17%. There is a clear and direct reason for this, which applies to many countries in Africa and is something that I have come across quite regularly in my career. The majority of the country, people who live in less affluent areas, use feature phones. Developing a web experience that is optimised for feature phones is a struggle, as some of you may be able to relate. This is something that very few developers ever take into account in their work, if at all."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"httpgsstatcountercommobiletablet-browser-ZA-monthly-201504-201604 gs.statcounter.com The most popular search engine here is Google, dominating an incredible 94% of the market. Other Search Engines, namely Bing (4%), Yahoo! (1%), DuckDuckGo (0.05%) barely make a dent in the user base."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When it comes to mobile operating systems, Android dominates the market with 43% usage. iOS only takes 5.46% of market share."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Connectivity"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa has 24.9 million active internet users; that’s less than half the current population. This figure includes access from both fixed line devices (laptops) as well as mobile devices."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The internet, along with its connectivity and speed, is a great complaining point amongst South Africans–a real pity as an ever-growing part of our population depends on it. For home use, ADSL packages start around R200 per month, this is uncapped data, unlimited devices but it is a shaped line."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It’s not uncommon to overhear a fellow office colleague all of a sudden shout “Is it just me, or is the internet really slow?!”. Certain times of the day it can be unbearable. This is especially true mid-morning and lunchtime, as well as between 7pm-9pm in the evening when everyone comes home."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If your ADSL isn’t up to scratch, you spend more time buffering Netflix (which launched the beginning of the year) than you do streaming. Services like Hulu, Spotify, Pandora, and Soundhound are not accessible in South Africa. There is no ban on the services, but the businesses themselves haven’t opened up to this territory yet. Except, that is, for Apple Music."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To get around this, VPNs are commonly implemented, but for the most part, unless you are technologically inclined and interested in setting a VPN up on your home network, no one is really interested or willing to do so."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Other than the above services, there are no bans on Facebook, Twitter or Youtube, nor on Google, Bing or Duck Duck Go. Which is just as well; we don’t have any real local alternatives to those services."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Mobile"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"South Africa has almost 80 million mobile connections, and mobile surpassed desktop browsing here back in 2014. This rise was thanks in no small part to adoption of Opera."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Mobile data is a bit of crutch for South Africans. It’s expensive for one, but coverage across networks can be seriously problematic."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The most popular activities that consume mobile data are email and instant messaging; the most frequently used service being WhatsApp with Social Media and Facebook Messenger trailing closely behind."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Prepaid-based data (Top Up) is a lot more expensive than contract-based data. For 1GB of data, you will pay R149 - R180 / month. 10GB of data is priced anywhere from R599 - R650 / month. There are no contracts that I’ve managed to find through my research which provided unlimited data."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The fastest bandwidth networks allow is 4G & LTE on supporting mobile devices. This fact also takes into account your location. Signal coverage of 4G and LTE is largely situated in city centres. For those who can afford it, most households will have an ADSL connection, so data is largely reserved for getting from A to B and public areas."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"One thing we haven’t really managed to crack here in South Africa is good public wifi. The odd coffee shop will offer you free, uncapped wifi, but they are few and far between and the chances of it being in good working order is slim."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The future is always difficult to predict, even in an industry where practicality and logic prevails. In the coming years we’ll see South Africa focus on longevity. Each year that the country moves forward in terms of remedying poverty and crime puts forth a new generation of smart individuals ready to take to the internet."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Technologies like Node.js and React will become more prevalent in required skills. Eventually, our internet and mobile data infrastructure will catch up with the rest of the world and we’ll move past the sluggish delivery of data. We’ll also see a bigger movement in the community, focusing on seeking attention for the South African market when it comes to WordPress and startup culture, much of which has already begun. I expect–I hope–that these opportunities will also spread to other countries in Africa."}]},{"_type":"block","markDefs":[{"_key":"68b3202594e7","_type":"link","href":"https://webdesign.tutsplus.com/a-guide-to-setting-up-vs-code-for-front-end-development--cms-30461t"}],"style":"normal","children":[{"_type":"span","marks":["em"],"text":"Read the original article on "},{"_type":"span","marks":["em","68b3202594e7"],"text":"Envato Tuts+"}]}]}
{"_type":"post","_createdAt":"2023-11-27T07:35:07.322+02:00","publishedAt":"2023-11-27T07:35:07.322+02:00","title":"Understanding Chromes Coverage Panel","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When we work on complex projects, JavaScript and CSS often get the jump on us. Before we know it, thousands, if not tens of thousands, of bytes are being transferred and executed in the browser - most of which we do not need to load."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"What is Code Coverage?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Code coverage derives from a computer science concept called \"test coverage\". From Wikipedia:"}]},{"_type":"block","style":"blockquote","markDefs":[],"children":[{"_type":"span","marks":[],"text":"Test coverage is a percentage measure of the degree to which the source code of a program is executed when a particular test suite is run."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"A program with high test coverage means that more or all of its source code gets executed during unit testing, implying that the code base would have a lower chance of undiscovered bugs."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Code Coverage is different when it comes to JavaScript and CSS. Of course, you can still write unit tests for JavaScript, but within the context of the browser environment, JavaScript and CSS coverage mean the following:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"CSS: what percentage of CSS gets utilised when it loads into the browser? More precisely, what percentage of nodes from the CSSOM get matched to nodes in the DOM?"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"bullet","children":[]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"JavaScript: what percentage of JavaScript functionality is executed by the browser?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"If we determine just how much of the referenced JavaScript and CSS assets are utilised by the browser, we can refactor or reimplement files to be more conscious of what they try to achieve on the page. If we only load the JavaScript and CSS required for the page, this reduces bundle sizes, transfer sizes and overall Paint time, leading to a great user experience."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Auditing Code Coverage in Chrome"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Many Chrome users must be aware of the Code Coverage panel in DevTools. To view it, open DevTools, go to the context menu, click \"More tools\", and select \"Coverage\"."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When the panel opens, you have two options:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Reload the page with a Code Coverage Audit"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Record code coverage attributed to your interactions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"After each audit, the panel will list all JavaScript and CSS-loaded resources. Clicking on a line item will display the source code with a vertical bar down the left. Sections of the bar will be coloured green or red."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Code blocks will be highlighted in red or green when reviewing the Sources panel for each request. Any code block highlighted in red is code that got loaded but not executed by the browser. The line item will also summarise the Total Unused Bytes and a percentage measure of unused code."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"How to handle poor Code Coverage"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Handling poor Code Coverage can be a challenge. It's hard work and may lead you down a path of a complete refactor. I grimace at the word \"refactor\", so let's chat about some higher-level wins first."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"CSS Wins"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Several tools are doing an excellent job of ensuring 100% CSS Code Coverage. PurgeCSS (leveraged by Tailwind) does a great job of removing unused CSS styles. You can implement PurgeCSS into any tech stack, so you are not married to Tailwind if you're not a fan."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Even WordPress Gutenberg released a nifty filter for only loading Block Library CSS when a block is on the page."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Using more modern CSS approaches can yield incredible results in the React space. You can leverage CSS Modules, Vanilla Extract and Linaria to help reduce the impact of unused CSS."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"JavaScript Wins"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"There are some minor wins in the actual execution of JavaScript. Optimising code for a high percentage measure is about what approach you're taking to the feature you're trying to build and when and how that feature gets executed in the browser. More often than not, it boils down to planning and architecture."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's worthwhile looking into recommended performance best practices like Route-based Code Splitting, Tree Shaking, Dynamic Import, Facade Patterns and Import on Interaction/Visibility."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Build Tool Wins"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Webpack is a sophisticated build manager that aids in code-splitting, tree-shaking, and other JavaScript wins that can, without a doubt, speed you along on the coverage front. As this space constantly evolves, keep looking for newer versions of build tools or even new build tools offering more performant features."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Dead Ends / Not Worth The Effort"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When it comes to gaining a high percentage measure with code coverage, two scenarios that will stop you dead in your tracks are:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"3rd-party JavaScript and CSS"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"Frameworks and Libraries."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Generally speaking, we have little control over how third-party scripts execute. In this case, we only have three options:"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"retain the script, remove the script, determine a way to defer the script execution."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Frameworks and Libraries are more complex. Removing unused code from even the fastest frameworks is only sometimes possible."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's also essential to understand where the necessity of this fits in concerning the state of performance on your site. There's no need to optimise everything if it doesn't yield valuable results."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Planning and Refactoring"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Technology stacks and architectures are your friends here. Each technology stack will expect something slightly different from you when selectively deciding how to deliver code that receives a high code coverage percentage."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"High Code Coverage is a gateway (in the long term) for bullet-proof performance. Ensure JavaScript code gets written to best performance practices. A well-moulded build configuration and careful planning of site features can also bolster successful attempts at increasing code coverage."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"High-percentage code coverage is a great way to ensure a lightweight and performant website. Many tools are available to help you identify, audit and remediate code that may have poorer-than-expected coverage across your site. Remember that your options become limited as soon as you buy into 3rd-party scripts and complex frameworks/libraries."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The chances of 100% code coverage across both CSS and JavaScript are likely unrealistic. You may also have an incredibly complex site that warrants large payloads, making attaining impactful code coverage more difficult."}]}]}
{"_type":"post","_createdAt":"2023-01-17T07:35:07.322+02:00","publishedAt":"2023-01-17T07:35:07.322+02:00","title":"Understanding The Environmental Impact of Slow Websites","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As countries worldwide struggle with power generation and carbon emissions, the environment has become an increasingly important talking point. Every day, millions, if not billions, of people consume and generate content on the internet, accessed through a plethora of devices 24 hours a day."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"We need to consider the effect of the internet on our carbon footprint. Slow, content-heavy websites and apps have a huge environmental impact that should be discussed more."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"In this article, I will discuss the carbon footprint of websites, what we can do to reduce the effects of carbon emissions online, what tools we can use to monitor and measure carbon emissions, and provide extra reading and best practices for keeping your websites green."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"How Websites Contribute to Carbon Emissions"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"When we talk about carbon emissions, we’re talking about the energy (electricity) required to maintain a website online. Energy output on the web is not one-dimensional. The user's device, the server hosting the website, and the data centre handling the request require energy to do their respective jobs."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Not only is energy required for “hosting” or “serving” the website, but the integrity of the infrastructure, such as cooling down servers, also contributes. The more servers, the more infrastructure, and the more traffic mean that a large-scale website will contribute to more emissions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Not every website is made equal. A significant publisher may include rich media like videos and large JavaScript files, requiring higher payloads and more bandwidth."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is where performance optimisation plays an important role."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"The Impact of Data Centres on Carbon Emissions"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I have no doubt that most of you reading this post are familiar with “the edge” - a new computing term that attempts to serve static files and data as close to a user’s location as possible."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"This is a paradoxical concept when it comes to carbon emissions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Data centres are required in geographical locations to be able to proxy and serve requests made by end users. A data centre is a physical place in the real world, with servers running 24/7. Big companies like Amazon and Google have data centres all over the world in an attempt to serve their content faster."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"To serve content more efficiently, data centres accumulate a substantial carbon footprint."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Strategies for Reducing The Carbon Footprint of a Website"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"There are multiple ways to improve the carbon footprint of a website. However, as most active individuals in the performance industry will tell you, it's not a one-size-fits-all approach."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Some strategies must take a step back to allow business units to perform their functions successfully. Ensuring a website is fast and lightweight can reduce its carbon footprint. Here are eight high-level strategies to review if you are interested in improving the carbon footprint of your website:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"SEO:"},{"_type":"span","marks":[],"text":" the more optimised a page is, the more easily users can find the information they need through Google and similar platforms. The less time they spend looking through results they do not need, the less energy they consume."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"UX:"},{"_type":"span","marks":[],"text":" User Experience is an essential factor in reducing carbon emissions. Suppose users can perform the actions they need to in an optimised and streamlined manner. In that case, we can optimise a user's flow from visiting a site to purchasing a product, reducing wasted resources and energy."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Images:"},{"_type":"span","marks":[],"text":" can have a heavy impact on carbon emissions. There are plenty of ways to optimise images from a technical standpoint, but you should ask yourself if the number of photos your site requests is necessary. Remember, the more images rendered on a site, the harder your device works - this compounds the energy requirement."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Reduce Rich Media"},{"_type":"span","marks":[],"text":" - Rich media, like videos, generally requires more resource-intensive transfers and computational power."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Hosting Providers:"},{"_type":"span","marks":[],"text":" Choose hosting providers with a high PUE (Power Usage Efficiency) rating"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Use Dark Mode"},{"_type":"span","marks":[],"text":" - darker colours require less energy on the user's device."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Static Files and The Edge:"},{"_type":"span","marks":[],"text":" Where possible, use static websites and host them on “the edge” - this inevitably leads to lower payloads, quicker page speed and more optimised delivery."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Use less JavaScript:"},{"_type":"span","marks":[],"text":" JavaScript is a giant resource hog - try to ensure that your website uses as little JavaScript as humanly possible."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Consider using an energy-efficient hosting provider as a website owner. “Green” hosting providers use renewable energy to power and cool-down server infrastructure, which can dramatically reduce carbon emissions."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Valuable Tools for Monitoring and Measuring Carbon Emissions"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Below are some tools I have recently come across that attempt to provide insight into the carbon footprint of a website."}]},{"_type":"block","markDefs":[{"_key":"352fa0212179","_type":"link","href":"https://branch.climateaction.tech/issues/issue-4/co2js/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["352fa0212179"],"text":"Co2.js"},{"_type":"span","marks":[],"text":" is a JavaScript package that runs on your site and advises you on your carbon emission footprint."}]},{"_type":"block","markDefs":[{"_key":"9ae1b5571ee5","_type":"link","href":"https://www.websitecarbon.com/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["9ae1b5571ee5"],"text":"Website Carbon Calculator"},{"_type":"span","marks":[],"text":" is a helpful calculator to help you understand your current carbon footprint."}]},{"_type":"block","markDefs":[{"_key":"52480ab22c06","_type":"link","href":"https://greenhost.net/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["52480ab22c06"],"text":"Greenhost"},{"_type":"span","marks":[],"text":" is a zero-carbon emission hosting provider."}]},{"_type":"block","markDefs":[{"_key":"6d87bd8c4d1e","_type":"link","href":"https://sustainablewebdesign.org/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["6d87bd8c4d1e"],"text":"Sustainable Web Design"},{"_type":"span","marks":[],"text":" - is an excellent resource for getting acquainted with the principles and considerations for building green websites."}]},{"_type":"block","markDefs":[{"_key":"8a8af5d20ff7","_type":"link","href":"https://ecoping.earth/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["8a8af5d20ff7"],"text":"EcoPing"},{"_type":"span","marks":[],"text":" provides an index of popular sites and tracks their carbon footprint."}]},{"_type":"block","markDefs":[{"_key":"1cc7cd7ccfa6","_type":"link","href":"https://github.com/jacklenox/susty"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["1cc7cd7ccfa6"],"text":"Susty"},{"_type":"span","marks":[],"text":" is a low-carbon emission WordPress theme."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"The Sustainable Web Manifesto"}]},{"_type":"block","markDefs":[{"_key":"aa0ee170fe57","_type":"link","href":"https://www.sustainablewebmanifesto.com/"}],"style":"normal","children":[{"_type":"span","marks":[],"text":"The "},{"_type":"span","marks":["aa0ee170fe57"],"text":"Sustainable Web Manifesto"},{"_type":"span","marks":[],"text":" is a collection of guiding principles that can help agencies and developers build a more lightweight and efficient web. The following principles guide the manifesto:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Clean:"},{"_type":"span","marks":[],"text":" The services we provide and use will be powered by renewable energy."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Efficient:"},{"_type":"span","marks":[],"text":" Our products and services will use the least energy and material resources possible."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Open:"},{"_type":"span","marks":[],"text":" The products and services we provide will be accessible, allow for the open exchange of information, and allow users to control their data."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Honest:"},{"_type":"span","marks":[],"text":" Our products and services will not mislead or exploit users in their design or content."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Regenerative:"},{"_type":"span","marks":[],"text":" The products and services we provide will support an economy that nourishes people and the planet."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Resilient:"},{"_type":"span","marks":[],"text":" The products and services we provide will function in the times and places where people need them most."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Further Reading"}]},{"_type":"block","markDefs":[{"_key":"3cc74ca1e01f","_type":"link","href":"https://yoast.com/carbon-footprint-of-website/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["3cc74ca1e01f"],"text":"\"The Carbon Footprint of Your Website and How to Reduce it\""},{"_type":"span","marks":[],"text":" - Yoast"}]},{"_type":"block","markDefs":[{"_key":"06dc77231b63","_type":"link","href":"https://www.climateimpact.com/news-insights/insights/infographic-carbon-footprint-internet/"},{"_key":"6a778b20852d","_type":"link","href":"http://ClimateImpact.com"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["06dc77231b63"],"text":"\"The Carbon Footprint of the Internet\""},{"_type":"span","marks":[],"text":" - "},{"_type":"span","marks":["6a778b20852d"],"text":"ClimateImpact.com"}]},{"_type":"block","markDefs":[{"_key":"dd32e475d482","_type":"link","href":"https://www.smashingmagazine.com/2019/01/save-planet-improving-website-performance/"}],"style":"normal","level":1,"listItem":"bullet","children":[{"_type":"span","marks":["dd32e475d482"],"text":"\"How Improving Website Performance Can Help Save The Planet\""},{"_type":"span","marks":[],"text":" - Smashing Magazine"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The internet is a great contributor to carbon emissions. As web performance techniques become more and more solidified into development cycles, we’ll hopefully be able to offset our carbon footprint."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"It's essential to know how websites affect carbon emissions and what we can do to reduce that footprint. Ensure that you work with cross-discipline departments to provide a fast and efficient user experience on your site. Use the tools available to you to monitor and measure carbon emissions."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The goal… let's make the web a sustainable place for everyone."}]}]}
{"_type":"post","_createdAt":"2023-02-07T07:35:07.322+02:00","publishedAt":"2023-02-07T07:35:07.322+02:00","title":"Waking Up Early: A Crash Course for Software Engineers","body":[{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Table of contents"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Introduction"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Let's rewind the clock to the 27th of March, 2020. A year that changed a lot about life as COVID descended upon each and everyone one of us. That day, a Friday, was the first day of the hard lockdown in South Africa. Like many of you reading this article, I was shocked and wondered how long I'd be left to survive the jail sentence handed down to me."}]},{"_type":"block","markDefs":[{"_key":"a3cb639b8515","_type":"link","href":"https://www.amazon.com/AM-Club-Morning-Elevate-Life/dp/1443456624"},{"_key":"a098492bbf95","_type":"link","href":"https://www.amazon.com/Monk-Who-Sold-His-Ferrari/dp/0062515675#:~:text=The%20Monk%20Who%20Sold%20His%20Ferrari%20tells%20the%20extraordinary%20story,of%20passion%2C%20purpose%20and%20peace."}],"style":"normal","children":[{"_type":"span","marks":[],"text":"On the Thursday afternoon before lockdown struck, I was in a bookstore and picked up \""},{"_type":"span","marks":["a3cb639b8515"],"text":"The 5 AM Club by Robin Sharma"},{"_type":"span","marks":[],"text":"\". I was instantly intrigued. I had read one of Robins's books ("},{"_type":"span","marks":["a098492bbf95"],"text":"The Monk Who Sold His Ferrari"},{"_type":"span","marks":[],"text":") some years before and enjoyed his storytelling. I purchased the book and read it through the first weekend of lockdown."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I had heard about the concept of the 5 AM Club multiple times leading up to that point, always knowing it was something I wanted to find out more about. Adapting to the 5 AM Club gave me a sense of control, considering how much of my life was now out of my control."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"I wasn't born a night owl. In fact, by 4 PM, I'm usually pretty useless to the world. However, waking up has always been easy for me. I have boundless energy in the morning and, to be honest, quite enjoy the peace in the world during the early hours of the day. I know, right? It's the complete antithesis of what software engineers are accustomed to."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"What if I told you, though, that following the practices of the 5 AM Club has helped me gain two promotions over two years, learn new technologies and languages, and even set up a side hustle or two? There are many benefits to waking up early, both professionally and personally, and I intend to convince you of them!"}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"What is The 5 AM Club?"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"So what exactly is The 5 AM Club? First and foremost, it's a book. However, I would say it's now closer to philosophy or methodology. The 5 AM Club follows the 20/20/20 rule, as set out in the book:"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"20 minutes of intense exercise"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"20 minutes goal setting"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":[],"text":"20 minutes learning a new skill"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"While the premise of The 5 AM Club is based on these three tenants, once you understand the reasons for each tenant, you can easily change, arrange and update them to suit your needs or align with your goals."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"As the Dalai Lama said: \"…know the rules well so that you can break them effectively.\""}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Waking up at 5 AM allows you to make space in your day before the world gets busy. The 5 AM Club has gained popularity among business leaders and entrepreneurs, and it can be elegantly applied to the careers of software engineers. Let's discuss some of the significant benefits of practising The 5 AM Club."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Benefits of the 5 AM Club"}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Achieve your career goals:"},{"_type":"span","marks":[],"text":" in my own experience, I leveraged the 5 AM Club to achieve my career goals. In 2020, I was a humble Senior Front-end Engineer; a year later, I became a Lead Front-end Engineer and, one year after that, an Associate Director of Front-end Engineering. This was a choice I made, dedicated to my career. Luckily, good fortune came into play, but I used my early morning hours to go the extra mile at work, and it paid off."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Balance your personal life:"},{"_type":"span","marks":[],"text":" not everything in life is about work. Your health, relationships and family are just as important. I don't have kids, but I have a committed relationship with my partner, two dogs to look after, and a house to run. You can use the early hours to spend time with your partner, exercise together, take the dogs for a walk, and plan travel/life goals."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Learn a new skill:"},{"_type":"span","marks":[],"text":" another great way to leverage the 5 AM Club is to spend time learning new skills. Whether that's a new programming language, a new library or a tech stack, even dedicating an hour a day to learning something new that will help your career or personal life can take you very far and make you more valuable in your position."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Increase productivity and efficiency:"},{"_type":"span","marks":[],"text":" as I had focused on my career, waking up before everyone else allowed me to get a jump start on my day. I would deliver project requests or feedback before anyone had even woken up. Code reviews were done before my colleagues were awake, and I could solve difficult bugs in solitude. Trust me; this makes project managers love having you on their team and makes you stand out as an overachiever."}]},{"_type":"block","markDefs":[{"_key":"d32691ff87e7","_type":"link","href":"https://www.dainemawer.com/articles/five-surprising-benefits-of-intermittent-fasting-for-software-engineers"}],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Improve your health:"},{"_type":"span","marks":[],"text":" you can also dedicate the early hours to improving your health. Coupled with "},{"_type":"span","marks":["d32691ff87e7"],"text":"intermittent fasting"},{"_type":"span","marks":[],"text":", you can get a head start on weight loss, exercise, planning meals or researching healthy alternatives. In my experience, it allows me to catch waves or run 10km without feeling the pressure of needing to be available on Slack."}]},{"_type":"block","markDefs":[],"style":"normal","level":1,"listItem":"number","children":[{"_type":"span","marks":["strong"],"text":"Start a side hustle:"},{"_type":"span","marks":[],"text":" software engineers have a tremendous competitive edge over many would-be entrepreneurs - they can build and execute websites or applications for free! This makes starting a side hustle easy enough if you have a great idea. The other problem is time, which the 5 AM Club solves. Consistency is key; dedicating an hour or two a day to your side hustle can pay huge benefits in the long run."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Breaking Old Habits"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Perhaps the most challenging part of achieving \"5 AM Club\" status is breaking the habit of waking up late. This barrier to entry is difficult for most people, never mind tired software engineers who have been staring at a screen and debugging complex logic problems."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Recent studies show that it takes around 28 days to break a habit. Thus, if you're serious about breaking your sleep cycle, it's going to take nearly a month to get used to waking up at 5 AM. This wasn't all that hard, as South Africa has relatively mild seasons. Don't get me wrong; winter is still cold, so I would not advise you to begin forming this habit in the dark. Wait for summer to arrive and let the sun help you out. It's far easier to rise with the sun than without it."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"Be patient with yourself, and begin with the 20/20/20 method. It's far easier to get used to than dedicating yourself to one direct benefit, as listed above."}]},{"_type":"block","markDefs":[],"style":"h3","children":[{"_type":"span","marks":[],"text":"Conclusion"}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The 5 AM Club has many benefits that can help software developers improve their health, advance their careers, balance their personal lives and help them achieve greatness. Breaking the habit of waking up late will be difficult, but you'll soon reap the benefits of this widespread practice."}]},{"_type":"block","markDefs":[],"style":"normal","children":[{"_type":"span","marks":[],"text":"The 5 AM Club is a way of gaining control in a chaotic and disruptive life for all of us. It's a philosophy and practice that can take you as far as you want. Buy the book, read it and start waking up early!"}]}]}