Over my 26 years of programming, developing applications, snippets, servers, APIs etc, I have been through a lot of different coding setups, editors, environments and so on. To sum up parts of my experience, I am attempting to disclose to you how you can have the best frontend developer experience. However, I am not going to tell you how to set up your editor directly. Every developer has a different opinion on which extension, theme, setting etc that is best, my self included. I love standards, especially standards that promotes effecient workflows, clean code and helps both the individual programmers and cooperating teams when developing applications. These are the standards I found to give me the best frontend developer experience: As described on the Semantic Versioning website, semantic versioning (semver) consists of three numbers: MAJOR, MINOR, and PATCH. Each number is incremented in different circumstances: Using semver helps a great deal for developers, and non-developers to understand what has changed, since the changed number in the version reflects what the actual change consists of. The perfect commit message should have certain qualities: Use the imperative, present tense. It is easier to read and scan quickly: 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 helps you write better commit messages! Install it: Update After you have staged your files for commit, run When you have chosen the type of commit you are commiting, press enter, and you can now enter a commit header: After you have pressed enter, you can now enter an optional commit body, that has more space to descrive the change the commit adds: After that, you will get an option to indicate if the change is a breaking change or not. If the change is NOT a breaking change, leave this field empty: You now have an option to add an issue ID for tracking the commit to a relevant task. This works for Jira (if you have the service connection) and github issues. When pressing enter, the interactive tool will stop, and output the commit output: When you now do a If you entered a description and/or a issue ID, it could look like this: I think that linting, when the concept arrived, was for me one of the biggest features for me as a developer. It helped a great deal for project code that was worked on in a team, conforming to one set of format. It also made the code look and feel cleaner. My favourite setup for now, is to use Preferred plugins: For TypeScript projects, add these as well: My standard My standard Why 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. And The longer the line, the bigger the diff and the harder it is to track what has been chanced My standard We all have our preferences when it comes to functions, these are some of mine. I prefer readability, so I would like to read the function name, and know exactly what it does. When it comes to boolean functions or methods, I like to read them as the state they want to represent. If you have conditional returns in a function or method, I love to pull out early, and it makes it, for me, easier to follow the flow. Especially if you calculations that is not required to do before escaping. Ternary operators are great, but use it with caution, because it can lead to horrible readability. Please, do not use any logix in jsx! It promotes poor readability, and increases the number of lines in the file. If you have a large frontend applications, imports could be a huge mess, with long imports like: If you use TypeScript, you can add aliases in the And for webpack: So that the import would look like this instead: To be able to group imports, and to make the hierarchy clearer, you can use the Which would make this: turn into this: Having a good naming convention is key in making logical sense of the code, and makes it easier to grasp the concept of the code and to get a better overview over what the code does. Please, for the love of Cthulhu, do not have your naming convention like this: The import would then look like: Using an unecessary amount of bytes. It is better to do this: The import would then look like: It's cleaner, shorter and saves bytes! Establishing a well-organized and sensible folder structure is one of the most crucial and demanding aspects of managing a large-scale application. Before contemplating the division of the codebase into multiple applications using micro frontends, it is advisable to take certain steps to enhance the project-level architecture. This approach can facilitate a smoother transition if such a path is ever considered. The objective is to implement a form of modularization that enhances the comprehensibility of the codebase by defining clear boundaries between features. This strategy aims to minimize code coupling and reduce side effects for improved maintainability. This is how I recommend to set it up, based on what I feel works best for me. YMMV: All public files, but most likely, only Put helper scripts for inhouse usage/auxilliary tools here. All your static assets goes here. This includes: Shareable components goes here. These components should be grouped by the type of component, for example: If a component requires other components only specific to that component, you should have a child folder named However, if you have only one extra component, put it alongside the main component If a Each component can have a set of folders, similar to the Any configuration for your application goes here (not test configs or configs that should be in the project root). If you use a framework that uses contexts, put them here. The majority of the code should be placed in this location. To facilitate smoother maintenance and scalability, our objective is to house the bulk of the application code within the features folder. Each feature folder is intended to encompass domain-specific code related to a particular feature, ensuring organizational clarity. Shared application services and providers Config for 3rd party libs goes here, or if you require to have your own version of a 3rd party lib, and also have to compile it, put it here. If the 3rd party lib is not to be compiled by you, put it in Put pages here. Having all the pages in one place is very helpful but the logic inside them should be kept to the minimum. Put global styles here. If you use stores, put them here. Test related stuff, mocks, helpers, utilities etc. If you use TypeScript, puth types, interfaces and enums here. If the code is some standalone, drop in function or class, put it here. I love to get rid of secondary activites, L O V E it. So if I can automate it, I automate it! Changelog generation goes hand in hand with releases, or changes merged into the master. And since I use To orchestrate my releases, i use Update your scripts in And you can do This is the Use a decent CI/CD setup, I love GitHub Actions. I always use these actions, regardless of the type of project I am working on: If you use JSDoc, you can generate documentation based on that. If you use TypeScript, you can have automagic documentation generated with TypeDoc. My preferred plugins: Update your This will generate documentation based of your code and JSdoc annotation in the Why advocate for code minimization? Well, a primary objective in my approach to development, both professionally and as a human being, is to contribute to a better world. Streamlining and reducing code directly contribute to a lower carbon footprint by using fewer bytes and transmitting less data over the internet. While many developers already focus on optimizing applications for speed, the emphasis on sustainability is gaining prominence. The equation is straight forward: fewer bytes sent result in a faster application, leading to a reduced carbon footprint. It's a win-win scenario. To enhance your carbon footprint, you can apply the same principles used for optimizing and minimizing code. Additionally, there are other strategies to lessen the environmental impact in development, including: Repeat after me: Refactor, refactor, refactor. Continusly refactoring and pruning the application is to me kinda like the process of caring for a bonsai tree. Create reusable components, split out code, carefully select dependencies, IF you require them. In the mantra of reducing code. Do not curate a large repository, consider splitting up the repository in smaller repositories. If some parts of the code is rarely updated, split it out. If parts of your code could be reusable in other projects, split it out. Consider to open source it! You could also test out monorepos, for example with Lerna. Do not use dependencies for code you can create yourself (leftpad anyone?). Many dependencies are created as polyfills for features a native API did not have in the beginning, but they have support for it now. Like jQuery, moment, date-fns and lodash/underscore. Usefull links: Ask yourself these questions: One example is, when you create a PR to Another example, if the PR only have changes in files that in no way effect the frontend application features/look/appearance, do you really need to have a merge-check? In conclusion, achieving the best frontend developer experience involves a combination of standardization, automation, and code minimization. Standardizing practices, such as adopting semantic versioning for clear communication about changes, committing to readable and concise commit messages, and following consistent coding patterns, contributes to an efficient workflow and collaborative development. Linting, particularly using tools like ESLint in conjunction with Prettier, enhances code quality and maintains a clean and uniform codebase. Establishing meaningful naming conventions, returning early in functions, and avoiding nested ternary operators improve code readability and maintainability. Organizing code into a well-structured folder hierarchy, utilizing aliases for imports, and employing sorting and grouping for imports contribute to a more maintainable codebase. Additionally, having a clear folder structure, including features, services, pages, and utilities, is essential for managing large-scale applications. Automation plays a crucial role in streamlining development processes. Leveraging tools like Commitizen for standardized commit messages, Release-it for automated releases, and GitHub Actions for continuous integration and deployment enhances efficiency and reduces manual intervention. Code minimization, both in terms of reducing code size and optimizing development processes, is highlighted as a key principle. Refactoring code, adopting smaller repositories, minimizing dependencies, and optimizing build processes contribute to a smaller carbon footprint and a more sustainable development approach. In essence, the journey towards a better frontend developer experience involves a holistic approach that encompasses standardized practices, efficient automation, and a commitment to code minimization for improved sustainability and maintainability.Standardize
Semantic versioning
Commits
Commitizen
package.json
:npm run commit
or yarn commit
in your project root folder. You will get an interactive guide that helps you with your commits:git log
, you'll see the change:Linting
eslint
in combination with prettier
prettier
+ eslint
= prettier-eslint
β₯.eslintrc.json
:.prettierrc
:80
as printWidth
when most of the devs use widescreens?.stylintrc
Functions
Naming
Return early
Nested ternary operators
JSX
Imports
Aliasing
tsconfig.json
:Sorting and grouping of imports
import/order
rule with ESLint:Nomenclature
Folder structure
Applications
JavaScript Web Application
TypeScript Web Application
React Web Application
NodeJS Application
public
index.html
or any public static file.scripts
src/assets
src/assets/js
src/assets/img
src/assets/icons
(or src/assets/img/icons
)*.css
) in src/assets/css
src/assets/fonts
browserconfig.xml
/site.webmanifest
in src/assets/meta
src/components
Grouping
src/components/navigation
src/components/page-section
src/components/form
or src/components/input-elements
src/components/tables
src/components/feedback
Hierarchy
components
. For example, if you have a component named Table
in src/components/tables/Table
, and you want TableHeader
, TableFooter
, TableSort
components, put them in src/components/tables/Table/components/TableHeader/
etc.*.tsx/*.jsx
context, if a component require a set of code that returns different components based on input, it is a helper, and should be in src/components/tables/Table/helpers/
:src
setup:src/config
src/contexts
src/features
src/services
src/lib
src/assets/js/
.src/pages
src/styles
src/stores
src/test
src/types
src/utils
Automate
Changelog
commitizen
, creating changelogs is a walk in the park. I use Conventional Changelog with conventional-changelog-conventionalcommits
.Releases
release-it
, and it's basically fire and forget. You can do it with an interactive CLI, or like me, in an action.package.json
:npm run release
!.release-it.json
I use:CI/CD
Extrapolate
Documentation generation
package.json
scripts:/api
folder.Minimize
Reduce code
Smaller repositories
Reduce dependencies
Reduce builds
main
, you have a merge check, and most likely in that merge check, you have a build step and a test step. If the check is passing, do you really need to to a new check on main
?Conclusion