10 Clean Code Habits I Actually Use in Real Projects

Frontend Development  ·  Clean Code

10 Clean Code Habits I Actually Use in Real Projects

Most developers know what clean code should look like. The hard part is writing it under deadline pressure when the codebase is already a mess. These are the ten habits I keep coming back to, not from a textbook, but from five years of shipping real projects.

Habit 01

Name Things Like You're Writing Documentation

The fastest way to confuse your future self is to name a variable data, temp, or x. I treat every name as a micro-comment. If someone has to trace back three functions to understand what a variable holds, the name failed.

Bad

const d = await fetchUser(id);

Good

const userProfile = await fetchUser(id); // intent is immediately clear

Rule: if you need a comment to explain what a variable is, the name is wrong.

Habit 02

One Function, One Job

A function that fetches data, formats it, and renders UI is three jobs in a trench coat. When something breaks, you have no idea where to look. This one habit changed how I structure React components entirely.

Too much in one place

function UserCard({ id }) {
  const [user, setUser] = useState(null);

  useEffect(() => {
    fetch(`/api/users/${id}`)
      .then(res => res.json())
      .then(data => {
        const formatted = `${data.firstName} ${data.lastName}`;
        setUser(formatted); // fetch + format + set in one place
      });
  }, [id]);

  return <div>{user}</div>;
}

Split the concerns

function formatFullName(data) {
  return `${data.firstName} ${data.lastName}`; // pure, testable, reusable
}

function UserCard({ id }) {
  const user = useUser(id); // custom hook handles fetching
  return <div>{formatFullName(user)}</div>;
}

Habit 03

Keep Your Code Flat with Early Returns

Deeply nested logic is where bugs hide. I have a personal rule: if I need to scroll sideways to read my code, something is wrong. Early returns reduce nesting and make the happy path obvious.

Hard to follow

function processOrder(order) {
  if (order) {
    if (order.items.length > 0) {
      if (order.isPaid) {
        return fulfill(order); // actual logic buried deep
      }
    }
  }
}

Clear intent with early returns

function processOrder(order) {
  if (!order) return;
  if (order.items.length === 0) return;
  if (!order.isPaid) return;

  return fulfill(order); // happy path is now immediately visible
}

Habit 04

Delete Code You Do Not Use

Commented-out code, unused imports, dead utility functions sitting at the bottom of a file. These create noise, mislead the next developer, and quietly rot. I used to keep dead code "just in case." Git history exists for that exact reason.

// This is not a safety net. Remove it.
// import { oldHelper } from './utils/legacy';
// function deprecatedFormat(val) { ... }

// If it is not used in production, it should not be in the file.

If you need it again, you will find it in version control. Keeping dead code is a tax on every future reader.

Habit 05

Keep Components Small and Focused

A React component that renders a full page section, manages its own state, handles API calls, and applies conditional styles is not a component. It is a monolith. I aim to keep components under 80 lines. When a component grows beyond that, I look for what can be extracted.

// Instead of one giant component, break it into smaller pieces
function ProductPage() {
  return (
    <main>
      <ProductHeader />   // handles title, breadcrumb
      <ProductGallery />  // handles images
      <ProductDetails />  // handles description, price
      <ProductActions />  // handles cart, buy button
    </main>
  );
}

Habit 06

Use Consistent Formatting (Prettier + ESLint)

Code style debates are a waste of time. Tabs vs spaces, single vs double quotes, trailing commas: none of this should live in your head or in a PR comment. Set up Prettier and ESLint once, enforce it on save, and move on.

// .prettierrc — agree on this once, never argue again
{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 80
}

Consistency is not about preference. It is about reducing cognitive load every time someone reads the codebase.

Habit 07

DRY, But Do Not Over-Abstract

DRY (Don't Repeat Yourself) is good advice. Over-abstraction is where it goes wrong. I have seen utility functions so generic they are impossible to use without reading the source code. If you need to abstract, abstract for a real, repeated use case, not a hypothetical one.

// Over-abstracted: what does this actually do?
function handleData(input, type, config) { ... }

// Better: specific, clear, honest about its purpose
function formatCurrency(amount, currency = 'USD') {
  return new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency,
  }).format(amount);
}

Habit 08

Handle Errors Explicitly

Silent failures are the worst kind of bug. An empty catch block, a missing null check, an unhandled promise rejection: these do not crash your app immediately. They corrupt it slowly. I handle errors at the point where they can actually happen, not somewhere vague up the chain.

// Bad: swallows the error silently
try {
  const data = await fetchUser(id);
} catch (e) {}

// Good: handle it, log it, show the user something useful
try {
  const userProfile = await fetchUser(id);
  setUser(userProfile);
} catch (error) {
  console.error('Failed to fetch user:', error);
  setError('Could not load profile. Please try again.');
}

Habit 09

Write Comments That Explain Why, Not What

The code already says what it does. A comment that just restates the code is noise. What the code cannot say is why a decision was made. That is where comments earn their place.

// Bad: restates the obvious
i++; // increment i

// Good: explains why this exists
// Delay added to avoid rate-limiting on the third-party API
await sleep(500);

// Good: documents an intentional decision that looks wrong
// Using index as key here because this list never reorders
items.map((item, index) => <Item key={index} {...item} />)

A good comment saves the next developer from undoing something that was done on purpose.

Habit 10

Write Code for the Reader, Not the Machine

The machine will run anything you throw at it. The human reading your code three months from now deserves better. Clever one-liners, chained ternaries, and compressed logic feel satisfying to write. They are a nightmare to debug.

// Clever, but hard to scan quickly
const label = isAdmin ? 'Admin' : isMod ? 'Mod' : isGuest ? 'Guest' : 'User';

// Readable: anyone can follow this at a glance
function getUserLabel(user) {
  if (user.isAdmin) return 'Admin';
  if (user.isMod)   return 'Mod';
  if (user.isGuest) return 'Guest';
  return 'User';
}

The extra four lines cost nothing. The clarity they provide is worth far more than the "efficiency" of the one-liner.

Clean code is not about being clever. It is about being clear. Every habit above comes down to one thing: write code that the next person can read without a map. That person is usually you, six months later, under pressure, trying to ship a fix before an important deadline. Write for that version of yourself.