Skip to content
How Can We Improve Frontend Code Quality?

29 Apr 2025 · 4 min read

How Can We Improve Frontend Code Quality?

Writing code that is easier to maintain

Frontend

This article Toss Frontend Fundamentals records content organized while reading the document.

During the organizing process, the following articles/resources provided particular insights:

Rather than a simple summary, I'll explain based on the concerns I felt while actually applying it to my code.

💡 Four Criteria for Judging Code Quality

At Toss, the following four points are key when judging code quality:

Readability: How easy is it to read?

Predictability: Can you tell how it works just by looking at its name?

Cohesion: Are related things grouped closely together?

Coupling: Are there too many unnecessary contexts intertwined?

1. Readability – Code is ultimately for 'readers'

Keep code that doesn't run together visually separate

  • Separate conditional statements and branching logic to reduce reading context
  • Ex: if Rather than putting JSX directly inside, it's easier to read if you name the condition and move it up.
// Before
{isLoggedIn && user.isAdmin && <AdminDashboard />}

// After
const shouldShowAdmin = isLoggedIn && user.isAdmin;

{shouldShowAdmin && <AdminDashboard />}

Implementation Detail Abstraction (HOC vs Wrapper)

  • HOC: Favorable for logic reuse. Abstracted, so structural understanding can be difficult, but it's clean.
  • Wrapper: Clearly visible in JSX structure, so the flow is clear.
  • Choose according to purpose. Wrapper is natural for UI-centric flow, HOC for business logic.

Even for declarative code, 'appropriate level' is important

  • Blindly abstracting can be detrimental to maintenance.
  • In the SignUpForm example, explicit expression is better than abstraction when many variations are needed.

Name complex conditions

  • isEmpty, shouldShowTooltip, isUnauthorizedUser are descriptive names that greatly improve readability.
  • Conditional rendering can be made clearer too: if (조건) return null → if (shouldSkipRender) changes it to something like this, making it readable at a glance.

Eliminate Magic Numbers

  • 300, 24 * 60 * 60 * 1000 are values worth extracting into constants so their meaning is clear.
  • TIME_MS.DEBOUNCE.SEARCH, FIVE_MINUTES makes future changes much easier.

Organize in a top-down readable structure

  • Define error messages and conditional logic separately at the top.
  • If a single flow goes from top to bottom, it's much less confusing.

2. Predictability – You should be able to 'anticipate' by looking at the code.

Manage names to avoid overlap

  • The same name (http) used at multiple levels causes confusion.
  • httpService, httpLibrary should be distinguished by making their roles clear, for example.

Return the same hook with the same structure

  • useQuery hooks should return a query object; making this a team rule makes their behavior easier to predict.

Structure validation using Discriminated Unions

// Types
{ ok: true } | { ok: false; reason: string }

Reveal hidden logic

async function fetchBalance() {
  const balance = await http.get<number>("...\");
  logging.log("balance_fetched\");
  return balance;
}
  • Side effects should not be hidden like this → Extract them into separate functions and clearly expose the call structure.

3. Cohesion – Group related things together

Files frequently modified together should be in the same folder

  • Form.tsx, Form.styles.ts, Form.types.ts, Form.test.tsx should naturally be in the same folder.

Also, make magic numbers constants and keep them within the same context.

  • Time constants, UI delay values, etc., should be modularized with meaningful naming.

Korean variable names can also be valid depending on the situation.

  • Ex: isGFSC → 국가지방자치단체_공공단체_금융사_여부
  • When the domain context is important, Korean can actually be clearer (Toss example)

4. Coupling – Divide responsibilities as much as possible

Don't put too much into one hook

  • Separate image uploads, validation, state management, and API calls by role.
  • Dividing hooks makes testing easier and increases reusability.

Avoid abstraction that breaks things more by trying to avoid duplication

  • Duplication is better than complex abstraction.
  • hoilzz blog reference

When solving Props Drilling, use Context or query params

  • For deeply nested prop passing, use Context.
  • For sharing state between pages, query parameters might be clearer.

Conclusion: Code quality is a series of small choices

While it's difficult to significantly improve code quality all at once,

  • Naming conditions
  • Separating constants
  • Separating functions to clarify responsibilities

These seemingly minor choices accumulate to create truly good code.

And the criterion for that judgment is ultimately, whether someone other than myself can immediately understand this code..

"Good code is ultimately code that makes you 'think less.'"

Going forward, I want to continue to review and organize my code based on these four criteria (readability / predictability / cohesion / coupling).