Using the 5S principle in coding
A few years ago, I read The Toyota Way, driven by my curiosity about the origins of Agile software development. However, I became even more intrigued by Toyota’s manufacturing processes and their efficiency improvements. The book offered many valuable insights and practices to adopt. As a developer, the 5S principle particularly caught my attention. I began to recognize that we often apply most of these principles in our work, albeit not in a systematic way.
The 5S principle, derived from lean manufacturing, is a Japanese methodology aimed at improving efficiency and productivity through systematic organization. It stands for Sort, Set in Order, Shine, Standardize, and Sustain.
In this article, we’ll explore how each of the 5S principles can be applied to coding, using JavaScript as our primary example. We’ll discuss practical implementations, tools that can assist in this process, and how these principles can be integrated into your development workflow. By the end, you’ll have a comprehensive understanding of how the 5S principle can improve your coding practices, leading to cleaner, more efficient, and more maintainable code.
It’s important to note that software development is fundamentally different from manufacturing — we’re not mass-producing identical products, but rather engaging in a creative, logical and iterative design process. Therefore, we’ll focus on adapting these principles to fit the unique needs of software development, rather than applying them directly.
Remember, the following examples and practices are not exhaustive or universally applicable to all production environments, they serve as a starting point.
1. Sort (Seiri) — Remove unnecessary items from your workspace.
In the context of coding, this means identifying and eliminating unused code, dependencies, and other clutter that can make your codebase harder to manage and understand. Here’s an example of how to apply the Sort principle, along with tools that can help.
Identify Unused Code:
Start by identifying unused code, such as functions, variables, comments and imports that are no longer needed. ESLint is a popular JavaScript linting tool used to identify and fix such problems in your JavaScript code. It analyzes your code for potential errors and enforces a consistent coding style, which helps in maintaining code quality and reducing bugs.
Before:
// Unused function
function unusedFunction() {
console.log('This is never used');
}
// const request = new Request("https://example.org/post", {
// method: "POST",
// body: JSON.stringify({ username: "example" }),
// });
// Main function
function mainFunction() {
console.log('This function is used');
}
mainFunction();
import React from 'react';
import { useState } from 'react';
import { unusedFunction } from './utils';
const MyComponent = () => {
const [state, setState] = useState(0);
return <div>{state}</div>;
};
export default MyComponent;
After:
// Main function
function mainFunction() {
console.log('This function is used');
}
mainFunction();
import React from 'react';
import { useState } from 'react';
const MyComponent = () => {
const [state, setState] = useState(0);
return <div>{state}</div>;
};
export default MyComponent;
Identify and Remove Unused Dependencies:
Tools like depcheck
can help you find unused npm packages in your project.
npx depcheck
Unused dependencies
* lodash
npm uninstall lodash
2. Set in Order (Seiton) — Organize items to ensure efficiency and flow.
In the context of coding, this means structuring your project’s codebase in a logical and consistent manner, and arranging files and directories for optimal understanding, reusability and maintainability for developers.
Organize project structure:
Atomic design is a methodology for creating design systems by breaking down UI components into smaller, reusable parts. This approach enhances organization and reusability in your project.
project/
├── src/
│ ├── components/
│ │ ├── atoms/
│ │ │ └── Button.tsx
│ │ ├── molecules/
│ │ │ └── Form.tsx
│ │ ├── organisms/
│ │ │ └── Header.tsx
│ │ ├── templates/
│ │ │ └── PageTemplate.tsx
│ │ └── pages/
│ │ └── HomePage.tsx
│ ├── assets/
│ │ ├── images/
│ │ └── styles/
│ ├── utils/
│ │ └── helpers.ts
│ └── App.ts
├── public/
│ └── index.html
├── package.json
└── README.md
Feature-Based Structure: Organizing by feature ensures that all files related to a specific feature are grouped together, which makes it easier to manage and scale larger applications.
src/
│
├── features/
│ ├── UserManagement/
│ │ ├── components/
│ │ │ ├── UserProfile.tsx
│ │ │ ├── UserList.tsx
│ │ │ └── UserForm.tsx
│ │ ├── hooks/
│ │ │ ├── useUserData.ts
│ │ │ └── useUserList.ts
│ │ ├── api/
│ │ │ └── userApi.ts
│ │ ├── utils/
│ │ │ └── userFormatters.ts
│ │ └── index.ts
│ │
│ ├── Authentication/
│ │ ├── components/
│ │ │ ├── LoginForm.tsx
│ │ │ └── RegistrationForm.tsx
│ │ ├── hooks/
│ │ │ └── useAuth.ts
│ │ ├── api/
│ │ │ └── authApi.ts
│ │ ├── utils/
│ │ │ └── authValidators.ts
│ │ └── index.ts
│ │
│ └── Dashboard/
│ ├── components/
│ │ ├── DashboardLayout.tsx
│ │ ├── AnalyticsWidget.tsx
│ │ └── ActivityFeed.tsx
│ ├── hooks/
│ │ └── useDashboardData.ts
│ ├── api/
│ │ └── dashboardApi.ts
│ ├── utils/
│ │ └── dashboardHelpers.ts
│ └── index.ts
│
├── shared/
│ ├── components/
│ │ ├── Button.tsx
│ │ ├── Input.tsx
│ │ └── Modal.tsx
│ ├── hooks/
│ │ └── useForm.ts
│ └── utils/
│ ├── formatters.ts
│ └── validators.ts
│
├── App.ts
└── index.ts
Flat Structure:
src/
├── components/
│ ├── Header.ts
│ ├── Footer.ts
│ └── Navigation.ts
├── pages/
│ ├── Home.ts
│ ├── About.ts
│ └── Contact.ts
├── utils/
│ └── helpers.ts
└── App.ts
The above examples are for demonstration purpose only.
Remember, the best structure is one that makes sense for your specific project and team. Some teams prefer flatter structures, while others find more nested organizations helpful. The key is to discuss and agree on an approach that enhances your team’s productivity.
- Consider the trade-offs between deeply nested structures and flat organizations.
- Use clear, descriptive names for files and directories.
- Ensure files and directories are arranged logically for easy access and maintainability.
- Place utility functions in a
utils
directory. - Group related components in a
components
directory, following the atomic design principle or other patterns that fits your need. - Store static assets like images and styles in an
assets
directory.
3. Shine (Seiso) — Clean the workspace to maintain standards and identify problems.
In the context of coding, this means ensuring your code is clean, readable, and adheres to coding standards. While shining typically involves cleaning and organizing items, it also involves addressing and resolving issues. Regularly reviewing and improving your codebase ensures it remains efficient and easy to work with over time. By integrating these practices and tools into your development workflow, you can enhance code readability, maintainability, and overall project quality.
Code Formatting and Style:
Tools like Prettier and ESLint help ensure consistent code formatting and quality.
// Before formatting
function fetchData(){
return fetch('https://api.example.com/data').then(response => response.json()).then(data => {console.log(data);});
}
// After formatting with Prettier
function fetchData() {
return fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => {
console.log(data);
});
}
Performance Optimization:
Improve code performance by optimizing algorithms, reducing complexity, and using efficient data structures. Tools such as SonarQube will provide comprehensive insights.
// Before improvement (Optimization Needed)
function findMax(arr) {
let max = arr[0];
for (let i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
// After optimization
Math.max(...arr);
Regular Refactoring:
Clean and improve code without changing its functionality, enhancing readability and maintainability. It is important to identify areas of improvement on a regular basis and apply the fix. If you inherit a codebase from another team, refactoring for efficiency will be crucial.
// Before simplyfing (Needs Refactoring)
function calculateTotalPrice(products) {
let totalPrice = 0;
for (let i = 0; i < products.length; i++) {
totalPrice += products[i].price;
}
return totalPrice;
}
// After refactoring
function calculateTotalPrice(products) {
return products.reduce((total, product) => total + product.price, 0);
}
Regularly revisiting and refining your practices ensures long-term project success and developer productivity.
4. Standardize (Seiketsu) — Establish standards and procedures to maintain organization and efficiency.
Standardization in coding involves establishing and adhering to consistent practices, conventions, and guidelines across a project or organization. This principle helps improve code quality, readability, and maintainability while reducing errors and enhancing collaboration among team members.
Naming Conventions:
Use consistent and descriptive naming conventions for variables, functions, classes, and files. This practice makes your code more readable and maintainable. A few examples:
// Variables and functions use camelCase
const userName = 'JohnDoe';
let userAge = 30;
function fetchUserData() {
// Fetch user data
}
const calculateRadius = () => { /* ... */ }
// Classes use PascalCase
class UserProfile {
constructor(name, age) {
this.name = name;
this.age = age;
}
}
// Constants use UPPER_SNAKE_CASE
const MAX_RETRY_ATTEMPTS = 3;
Establishing and Using Style Guides:
Style guides define coding conventions and best practices to ensure consistency across the codebase. You can choose create one that meets your requirement or pick one from already existing style guides.
Airbnb JavaScript Style Guide: A popular style guide that covers various aspects of JavaScript coding.
Google Typescript Style Guide: Google’s style guide provides comprehensive coding standards and practices for Typescript.
// Before Standardization
function fetchData(){
return fetch('https://api.example.com/data').then(response => response.json()).then(data => {console.log(data);});
}
// After Standardization
function fetchData() {
return fetch('https://api.example.com/data')
.then((response) => response.json())
.then((data) => {
console.log(data);
});
}
Consistent Error Handling:
Establish a standardized approach to error handling and logging practices to ensure consistency and improve debugging.
// Before Standardization
function fetchData() {
return fetch('https://api.example.com/data')
.then((response) => response.json())
.catch((error) => {
console.error(error);
});
}
// After Standardization
async function fetchData() {
try {
const response = await fetch('https://api.example.com/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching data:', error);
throw error;
}
}
Testing Standards:
Define standards for writing and organizing tests, including naming conventions and coverage requirements.
Create test files using a consistent naming convention, e.g., filename.test.js
:
// fetchData.test.js
const fetchData = require('./fetchData');
test('fetches data from API', async () => {
const data = await fetchData();
expect(data).toBeDefined();
});
Capture and set a code coverage threshold:
It ensures that coverage reports are generated, sets a minimum coverage threshold, and specifies which files to include or exclude from the coverage analysis.
module.exports = {
collectCoverage: true,
coverageDirectory: 'coverage',
coverageProvider: 'v8',
coverageReporters: ['text', 'lcov', 'clover'],
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80,
},
},
collectCoverageFrom: [
'src/**/*.{js,jsx,ts,tsx}',
'!src/**/*.d.ts',
'!src/index.tsx',
'!src/serviceWorker.ts',
],
};
Version Control with Git:
Use version control systems like Git to maintain a standardized approach to code management, ensuring consistency and collaboration and use standardized Branch Naming Conventions:
- Feature branches:
feature/short-description
- Bugfix branches:
bugfix/short-description
- Release branches:
release/x.y.z
main
feature/user-authentication
feature/dashboard-improvements
bugfix/login-error
release/1.0.0
5. Sustain (Shitsuke) — Maintain and review standards regularly to ensure long-term adherence.
The fifth and the final principle of 5S, focuses on maintaining and continually improving standards over time. This principle ensures that the practices established by the previous four principles are adhered to and become a part of the regular workflow. In the context of software development, this involves continuous integration, regular code reviews, automated testing, and ongoing learning and adaptation to new technologies and practices.
Using Git Hooks
Using Git hooks with Husky allows you to automate checks and tasks at different stages of your Git workflow. This can help maintain code quality, ensure tests pass before commits are made, and enforce commit message conventions across your team.
// package.json
{
"scripts": {
"prepare": "husky install"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint && npm test",
"pre-push": "npm run build",
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
}
Continuous Integration and Continuous Deployment (CI/CD):
Implement CI/CD pipelines to ensure that code changes are automatically tested, integrated, and deployed. Below is an example for GitHub Actions.
Make every push or pull request to the main
branch to trigger the CI pipeline, ensuring that tests and linting are run automatically.
name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Lint code
run: npm run lint
Code Documentation:
Maintain code documentation to improve code readability and maintenance. Use tools like JSDoc to generate documentation.
/**
* Calculates the area of a rectangle.
*
* @param {number} length - The length of the rectangle.
* @param {number} width - The width of the rectangle.
* @returns {number} The area of the rectangle.
* @throws {Error} If either length or width is negative.
*
* @example
* // returns 50
* calculateRectangleArea(10, 5);
*
* @since 1.0.0
* @author Jane Doe <jane@example.com>
*/
function calculateRectangleArea(length, width) {
if (length < 0 || width < 0) {
throw new Error('Length and width must be non-negative');
}
return length * width;
}
Regular Code Reviews:
Conduct regular code reviews to ensure adherence to coding standards and best practices.
Code Review Checklist:
- Code adheres to style guide (e.g., Airbnb, Google).
- Functionality is correct and meets requirements.
- Code is clean, readable, and maintainable.
- Tests are present and comprehensive.
- Performance considerations are addressed.
Continuous Learning and Adaptation:
It’s crucial to stay current in the software industry, as it evolves rapidly. Continuous upskilling helps developers stay informed about new technologies, apply them to solve problems, and explore new career opportunities.
- Stay updated with the latest trends, technologies, and best practices in JavaScript development using online courses, books and videos, etc.
- Participate in JavaScript communities (e.g., Stack Overflow, GitHub).
- Attend conferences and meetups (e.g., JSConf, local JavaScript meetups).
Implementing these principles may seem daunting at first, especially in large, established projects or teams. However, the long-term benefits far outweigh the initial investment of time and effort. Cleaner, more organized code leads to fewer bugs, easier onboarding for new team members, and more efficient development cycles.
Remember, the goal of 5S in coding is not perfection, but continuous improvement. Start small — perhaps by implementing a linter in your project or establishing a code review process. As your team becomes comfortable with these changes, gradually introduce more 5S practices.