Makers of a sustainable future

Makers of a sustainable future

A better frontend experience?

*

A better frontend experience?

*

Alexander Vassbotn Røyne-Helgesen

Principal Engineer

ex-Vikinghorde

Hockey dad

Not a fan of Oilers

Alexander Vassbotn Røyne-Helgesen

Principal Engineer

ex-Vikinghorde

Hockey dad

Not a fan of Oilers

$ npm i @phun-ky/speccer

A better frontend experience?

YMMV

Standardize, Automate, Minimize, Extrapolate

The importance of standards

The importance of standards

The importance of standards

Versioning

  • Improved communication with release notes/changelog
  • Easier to reference
  • Predictability and consistence
  • Release feature transparency
  • Predictable integration with tools
  • Long term maintenance and cooperation
  • More control on the releases
  • Standard practice in the business
  • Rollback and patch management

The importance of standards

Semantic Versioning

The importance of standards

Semantic Versioning (SemVer) consists of three numbers: MAJOR, MINOR, and PATCH. Each number is incremented in different circumstances:

 

  • the MAJOR version when we make incompatible API changes (read: breaking changes),
  • the MINOR version when we add functionality in a backward-compatible manner, and
  • the PATCH version when we make backward-compatible bug fixes.

 

The importance of standards

9.2.10

-alpha

+3284aea4

MAJOR

MINOR

PATCH

prerelease

build

version core

optional

The importance of standards

YEAR

MONTH

BUILD

status

2024.12.4081

-alpha

version core

optional

The importance of standards

Using SemVer helps a great deal to understand what has changed, since the changed number reflects what the actual change consists of.

The importance of standards

Writing Effective Commits

The importance of standards

$ git commit -am "Added feature ... (past tense)"
$ git commit -am "clarify further the brokenness of C++. why the fuck are we using C++?"

The importance of standards

$ git commit -am "Add feature to alert admin for new user registration"
$ git commit -am "Elaborate on the inherent deficiencies of the C++ programming language"

The importance of standards

$ git commit -am "Add feature to alert admin for new user registration"
$ git commit -am "Elaborate on the inherent deficiencies of the C++ programming language"

The importance of standards

The reason behind using present tense is that the commit message is answering the question "What will happen after the commit is applied?".


If we think of a commit as an independent patch, it does not matter if it applied in the past. What matters is that this patch is always supposed to make that particular change when it is applied.

Commitizen

The importance of standards

Commitizen … uses a standard way of committing rules and from that foundation, it can bump version, create changelog, and update files.

The importance of standards

$ git log --pretty=format:'%h %s'.
3332279 style: 💄 Lint.
60f0ec8 fix: 🐛 Fix sticky header issues.
9f5fdcd feat: 🎸 Add read time.
55bf3ad feat: 🎸 Add copy code button and language string to code blocks.
813df4a refactor: 💡 Some adjustments.
79549ab refactor: 💡 Adjust code blocks.
6876cec fix: 🐛 Add label attribute to heading link.
8bf68c6 refactor: 💡 Comment out copy / language feat for prism.
9f97ebf feat: 🎸 Make header sticky in desktop mode.
95ceac0 style: 💄 Tune lint rules for markdown.
d7c3a13 feat: 🎸 Add `print.css`.
// In your project root
$ sudo npm install -g commitizen git-cz && commitizen init git-cz
{
  …
  "scripts": {
    …
    "commit": "npx git-cz",
    …
  },
  …
}
$ git log
commit 8f82e8189e614332b6c80d2ab8291aa59325b130 (HEAD -> main)
Author: Alexander Vassbotn Røyne-Helgesen <alexander+github.com@phun-ky.net>
Date:   Fri Jan 5 15:11:19 2024 +0100

    style: 💄 lint `src/styles/index.styl`

    For funsies

    ✅ Closes: #123

Linting

The importance of standards

The importance of standards

  • ESLint
  • Prettier
  • Putout

… when the concept arrived it was one of the biggest features for me as a dev.  

It made the code feel cleaner

The importance of standards

{
  "singleQuote": true,
  "jsxBracketSameLine": true,
  "tabWidth": 2,
  "printWidth": 80,
  "proseWrap": "always"
}
{
  "singleQuote": true,
  "jsxBracketSameLine": true,
  "tabWidth": 2,
  "printWidth": 80,
  "proseWrap": "always"
}

The reason why prettier uses 80 columns to format code is because this is the best heuristic we know. It's not perfect as you have seen but one interesting property is that it almost never looks terrible and most of the time it look reasonable.

 

The longer the line, the bigger the diff and the harder it is to track what has been chanced

The importance of standards

$ NO_ESLINT=1 npx putout src
src/utils/version.ts
 140:42  error   Use function to check type instead of 'typeof'  types/convert-typeof-to-is-type    
 265:4   error   Use function to check type instead of 'typeof'  types/convert-typeof-to-is-type    
 155:17  error   Swap 'instances.length - 1' with 'index'        conditions/apply-comparison-order  
 37:2    error   Use consistent blocks                           conditions/apply-consistent-blocks 
 45:2    error   Use consistent blocks                           conditions/apply-consistent-blocks 
 242:2   error   Use consistent blocks                           conditions/apply-consistent-blocks 
 60:9    error   Avoid useless 'else'                            conditions/remove-useless-else     
 66:9    error   Avoid useless 'else'                            conditions/remove-useless-else     

src/utils/authentication/index.ts
 17:19  error   Avoid useless 'async'  promises/remove-useless-async 

src/utils/converters/organisation-to-normalized-dropdown-item-type.ts
 16:56  error   Avoid useless 'return'  remove-useless-return 

src/utils/stop-place-icon/index.ts
 6:9    error   Use optional chaining  apply-optional-chaining/use 
 9:22   error   Use optional chaining  apply-optional-chaining/use 

src/utils/validation/user-profile-schema.ts
 32:8  error   Use object destructuring  apply-destructuring/object         
 34:8  error   Use consistent blocks     conditions/apply-consistent-blocks 

✖ 168 errors in 18 files
  fixable with the `--fix` option

Function Naming and Practices

The importance of standards

// Don't do this for content
export const checkForApples = (…): boolean => …;
// Don't do this for boolean returns
export const versionIsExpired = (…): boolean => …;

The importance of standards

// Do this when checking for content
export const hasApples = (…): boolean => …;
// Do this for boolean returns
export const isVersionExpired = (…): boolean => …;

The importance of standards

export const myFunction = (var) => {

  if(var){
    return [1,2,3,4,5];
  } else {
    return [];
  }
}

The importance of standards

export const myFunction = (var) => {

  if(!var) return [];

  return [1,2,3,4,5];
}

The importance of standards

Avoiding Nesting Ternary Operators

The importance of standards

const a = b === null ? false : b === 2 ? false : true;

The importance of standards

let a = false;

if (b === 2) {
  a = true;
}

The importance of standards

JSX Best Practices

The importance of standards

export const MyComponent = ({ isCool }) => {

  return (
    {isCool ? <MyCoolComponent/> : <MyNotSoCoolComponent/>}
  )
}

The importance of standards

export const MyComponent = ({ isCool }) => {
  if (isCool) return <MyCoolComponent />;

  return <MyNotSoCoolComponent />;
};

The importance of standards

Managing Imports

The importance of standards

import React from 'react';

import { Button } from '../../../../../../components/actions/Button';
{
  "compilerOptions": {
    …
    "paths": {
      "components/*": ["./src/components/*"],
      …
    }
  },
  …
}
{
  …
  alias: {
    components: path.resolve(__dirname, 'src/components'),
    …
  },
  …
}
import React from 'react';

import { Button } from 'components/actions/Button';

Sort Your Imports

The importance of standards

{
  …
  rules: {
    'import/order': [
      'error',
      {
        groups: [
          'external',
          'builtin',
          'internal',
          'parent',
          'sibling',
          'index'
        ],
        pathGroups: [
          { pattern: 'components', group: 'internal' },
          { pattern: 'utils', group: 'internal' },
          …
        ],
        pathGroupsExcludedImportTypes: ['internal'],
        alphabetize: {
          order: 'asc',
          caseInsensitive: true
        },
        'newlines-between': 'always'
      }
    ],
  }
  …
}

The importance of standards

Folder Structure

The importance of standards

$ struct
📦 my-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.js
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-ts-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-react-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 contexts
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 stores
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 App.tsx
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-express-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 config
 │ ├── 📁 lib
 │ ├── 📁 middlewares
 │ ├── 📁 router
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ └── 📄 index.ts
 └── 📄 package.json
$ struct
📦 my-static-html-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ │ ├── 📁 css
 │ │ ├── 📁 img
 │ │ ├── 📁 js
 │ │ ├── 📁 meta
 │ │ └── 📁 posts
 │ ├── 📁 components
 │ ├── 📁 pages
 │ ├── 📁 styles
 │ ├── 📁 utils
 │ └── 📄 main.js
 └── 📄 package.json
$ struct
📦 my-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.js
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-ts-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-react-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 contexts
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 stores
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 App.tsx
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-express-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 config
 │ ├── 📁 lib
 │ ├── 📁 middlewares
 │ ├── 📁 router
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ └── 📄 index.ts
 └── 📄 package.json
$ struct
📦 my-static-html-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ │ ├── 📁 css
 │ │ ├── 📁 img
 │ │ ├── 📁 js
 │ │ ├── 📁 meta
 │ │ └── 📁 posts
 │ ├── 📁 components
 │ ├── 📁 pages
 │ ├── 📁 styles
 │ ├── 📁 utils
 │ └── 📄 main.js
 └── 📄 package.json
$ struct
📦 my-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.js
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-ts-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-react-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 contexts
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 stores
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 App.tsx
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-express-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 config
 │ ├── 📁 lib
 │ ├── 📁 middlewares
 │ ├── 📁 router
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ └── 📄 index.ts
 └── 📄 package.json
$ struct
📦 my-static-html-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ │ ├── 📁 css
 │ │ ├── 📁 img
 │ │ ├── 📁 js
 │ │ ├── 📁 meta
 │ │ └── 📁 posts
 │ ├── 📁 components
 │ ├── 📁 pages
 │ ├── 📁 styles
 │ ├── 📁 utils
 │ └── 📄 main.js
 └── 📄 package.json
$ struct
📦 my-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.js
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-ts-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-react-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 contexts
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 stores
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 App.tsx
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-express-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 config
 │ ├── 📁 lib
 │ ├── 📁 middlewares
 │ ├── 📁 router
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ └── 📄 index.ts
 └── 📄 package.json
$ struct
📦 my-static-html-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ │ ├── 📁 css
 │ │ ├── 📁 img
 │ │ ├── 📁 js
 │ │ ├── 📁 meta
 │ │ └── 📁 posts
 │ ├── 📁 components
 │ ├── 📁 pages
 │ ├── 📁 styles
 │ ├── 📁 utils
 │ └── 📄 main.js
 └── 📄 package.json
$ struct
📦 my-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.js
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-ts-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-react-frontend
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 components
 │ ├── 📁 config
 │ ├── 📁 contexts
 │ ├── 📁 features
 │ ├── 📁 lib
 │ ├── 📁 pages
 │ ├── 📁 services
 │ ├── 📁 styles
 │ ├── 📁 stores
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ ├── 📁 …
 │ └── 📄 App.tsx
 │ └── 📄 main.ts
 ├── 📁 public
 └── 📄 package.json
$ struct
📦 my-express-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ ├── 📁 config
 │ ├── 📁 lib
 │ ├── 📁 middlewares
 │ ├── 📁 router
 │ ├── 📁 types
 │ ├── 📁 test
 │ ├── 📁 utils
 │ └── 📄 index.ts
 └── 📄 package.json
$ struct
📦 my-static-html-app
 ├── 📁 scripts
 ├── 📁 src
 │ ├── 📁 assets
 │ │ ├── 📁 css
 │ │ ├── 📁 img
 │ │ ├── 📁 js
 │ │ ├── 📁 meta
 │ │ └── 📁 posts
 │ ├── 📁 components
 │ ├── 📁 pages
 │ ├── 📁 styles
 │ ├── 📁 utils
 │ └── 📄 main.js
 └── 📄 package.json

Group the components!

The importance of standards

  • link, menus etc goes into src/components/navigation
  • header, footer, main, intro goes into src/components/page-section
  • input fields, form stuff goes into src/components/form or src/components/input-elements
  • tables, data-tables, comparison tables goes into src/components/tables
  • errors, loading, notification, toasts goes into src/components/feedback

The importance of standards

$ struct
📦 my-frontend
 ├── 📁 src
 │ ├── …
 │ ├── 📁 components
 │ │ ├── 📁 communication
 │ │ ├── 📁 content
 │ │ ├── 📁 meta
 │ │ ├── 📁 navigation
 │ │ ├── 📁 page-sections
 │ │ └── …
 │ └── …
 └── …

Max File Length

The importance of standards

module.exports = {
  …,
  rules: {
    …,
    'max-lines': ['error',{
      max: 50,
      skipBlankLines: false
    }],
  }
};
module.exports = {
  …,
  rules: {
    …,
    'max-lines': ['error',{
      max: 50,
      skipBlankLines: false
    }],
  }
};

Automate

Automate

Automate

Automating Releases

CI/CD

Automate

Release-it

Automate

Dependabot

Automate

version: 2
updates:
- package-ecosystem: 'npm'
  directory: '/'
  groups:
    minor-and-patch:
      applies-to: version-updates
      update-types:
      - "minor"
      - "patch"
    major-updates:
      applies-to: version-updates
      update-types:
      - "major"
  schedule:
    interval: "weekly"
  open-pull-requests-limit: 2
  allow:
  - dependency-type: direct
  - dependency-type: production
    commit-message:
      prefix: 'chore: 🤖 '

GitHub Actions

Automate

name: Publish
on:
  pull_request:
    types: [closed]
    branches: [ "main" ]
    paths:
      - '.github/**'
      - 'src/**'
      - 'api/**'
      - 'public/**'
      - 'package.json'
      - 'package-lock.json'
      - 'rollup.config.js'
      - 'tsconfig.json'
      - '.npmrc'
      - '.release-it.json'
      - 'README.md'
      - 'CONTRIBUTING.md'
      - 'CODE_OF_CONDUCT.md'
      - 'SECURITY.md'
      - '.postcssrc.cjs'
      - '.browserlistrc'
  workflow_dispatch:
jobs:
  publish:
    if: github.event.pull_request.merged == true
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0 # Checkout all branches and tags
          token: ${{ secrets.GH_TOKEN }}
      - name: Setup Node.js environment
        uses: actions/setup-node@v4
        with:
          node-version: '>=20.11.1'
      - name: Install dependencies
        run: | # Install and link dependencies
          npm i
      - name: "Release" # Interesting step
        run: |
          git config user.name "${{ github.actor }}"
          git config user.email "${{ github.actor}}@users.noreply.github.com"
          npm run release
        env:
          NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
          GH_TOKEN: ${{ secrets.GH_TOKEN }}
  notify:
    name: Notify failed build
    needs: publish
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - uses: jayqi/failed-build-issue-action@v1.2
        with:
          github-token: ${{ secrets.GH_TOKEN }}
name: Checks
on:
  pull_request:
    branches: [ "main" ]
    types: [opened, synchronize]
jobs:
  check:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
      - name: Setup Node.js environment
        uses: actions/setup-node@v4
        with:
          node-version: '>=20.11.1'
      - name: Install dependencies
        run:  npm i
      - name: Build
        run: npm run build
      - name: Test
        run: npm run test-ci
      - name: Upload coverage reports to Codecov
        uses: codecov/codecov-action@v4.0.1
        with:
          token: ${{ secrets.CODECOV_TOKEN }}
          slug: phun-ky/speccer
  notify:
    name: Notify failed check
    needs: check
    if: failure()
    runs-on: ubuntu-latest
    steps:
      - uses: jayqi/failed-build-issue-action@v1.2
        with:
          github-token: ${{ secrets.GH_TOKEN }}
name: "Pull Request Labeler"
on:
- pull_request_target

jobs:
  labeler:
    permissions:
      contents: read
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
    - id: label-the-PR
      uses: actions/labeler@v5
      with:
        sync-labels: true
'section: workflows':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - .github/workflows/**
'section: repo':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - '*'
'experience: developer':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - '.editorconfig'
              - '.eslintignore'
              - '.eslintrc.json'
              - '.eslintrc.js'
              - '.gitignore'
              - '.release-it.json'
              - '.renovate.json'
              - '.npmrc'
              - '.prettierrc'
              - 'tslint.json'
'context: github':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - .github/**
'context: docker':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - Dockerfile
'context: npm':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - '.npmrc'
'context: rollup':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - 'rollup.config.js'
              - 'rollup.config.mjs'
              - 'rollup.*.config.js'
              - 'rollup.*.config.mjs'
'mindless: docs':
  - any:
      - head-branch: ['^docs', 'docs']
      - changed-files:
          - any-glob-to-any-file:
              - '**/*.md'
'mindless: dependencies':
  - any:
      - changed-files:
          - any-glob-to-any-file:
              - 'package-lock.json'
              - 'yarn.lock'

# Add '✨ feature' label to any PR where the head branch name starts with `feat` or has a `feat` section in the name
'✨ feature':
  - head-branch: ['^feat', 'feat']
'problems: bug':
  - head-branch: ['^fix', 'fix']
'mindless: chore':
  - head-branch: ['^chore', 'chore']
'improvements: enhancement':
  - head-branch: ['^improvements', 'improvements']

# Add 'release' label to any PR that is opened against the `main` branch
release:
  - base-branch: 'main'
$ gh label clone entur/products-models --repo entur/vite-plugin-assets-json
name: Auto Assign
on:
  issues:
    types: [opened]
  pull_request:
    types: [opened]
jobs:
  run:
    runs-on: ubuntu-latest
    permissions:
      issues: write
      pull-requests: write
    steps:
    - name: 'Auto-assign issue'
      uses: pozil/auto-assign-issue@v1
      with:
          repo-token: ${{ secrets.GITHUB_TOKEN }}
          assignees: phun-ky
          numOfAssignee: 1

Extrapolate

Extrapolate

Extrapolate

Changelog generation

{
  …
  "plugins": {
    "@release-it/conventional-changelog": {
      "header": "# Changelog",
      "preset": {
        "name": "conventionalcommits",
        "types": […]
      },
      "infile": "CHANGELOG.md"
    }
  }
}

Documentation generation

Extrapolate

$ npm i -D typedoc
$ npm i -D typedoc-plugin-frontmatter typedoc-plugin-markdown typedoc-plugin-mdn-links typedoc-plugin-no-inherit typedoc-plugin-rename-defaults
{
  …
  "scripts": {
    …
    "docs:gen": "node ./node_modules/.bin/typedoc --entryPoints src --entryPointStrategy expand --gitRevision main --githubPages false --plugin typedoc-plugin-markdown --tsconfig tsconfig.json --hideInPageTOC --out api --readme none",
    …
  }
  …
}

Minimize

Minimize

Additionally, there are other strategies to lessen the environmental impact in development, including:

 

  • General reduction of code
  • Minimizing the code stored in your repository
  • Decreasing the number of dependencies in your project
  • Reducing the frequency of builds or the shipment of artifacts to other vendors

Minimize

Refactor Code

Minimize

Reducing Dependencies

Minimize

module.exports = leftpad;
function leftpad (str, len, ch) {
  str = String(str);
  var i = -1;
  if (!ch && ch !== 0) ch = ' ';
  len = len - str.length;
  while (++i < len) {
    str = ch + str;
  }
  return str;
}

Optimizing build processes

Minimize

  • Do you really need to install every dependency?
  • Can you reuse artifacts?
  • Do you really need to update snapshots?
  • Do you have to build for every release stage?

Minimize

Minimize

Smaller Repositories

What's next?

What's next?

What's next?

  • Find out what standards that works great for you AND your team
  • Get rid of secondary activities and ease your daily workflow with automation
  • Minimize technical debt and carbon footprint by refactoring and reduce code, remove unneeded dependencies and irrelevant builds.
  • Extrapolate information from code that you already have made

 

A better frontend experience!

Q&A

https://github.com/phun-ky
https://www.npmjs.com/~phun-ky
https://phun-ky.net/
https://codepen.io/phun-ky/
https://slides.com/phun-ky
https://www.linkedin.com/in/alexanderroyne
https://www.instagram.com/phun_ky/

A better frontend developer experience?

By Alexander Vassbotn Røyne-Helgesen

A better frontend developer experience?

Dive into the nitty-gritty of frontend development with me! In this talk, I'll share 24 years of coding insights, breaking down practical tips for an A+ developer experience. From semantic versioning hacks to commitizen magic, we'll spice up your coding life. Learn the dos and don'ts of naming functions, organizing components, and acing imports. But wait, there's more! Discover the sweet spot of automation using Commitizen, Release-it, and GitHub Actions. Plus, let's talk code minimization—why it matters and how it can make your life easier. Whether you're a coding pro or just getting started, this talk is packed with hands-on advice to level up your frontend game. Join me for a casual chat about cleaner code, smoother workflows, and sustainable coding habits. No fluff, just the good stuff! See you there!

  • 46