This article Toss Frontend Fundamentals records content organized while reading the document.
During the organizing process, the following articles/resources provided particular insights:
- How to Avoid Wrong Abstraction
- Counterexample to Obsession with Rendering Optimization
- Toss Development Team's Proposal for Korean Variable Names
- What is Frontend Declarative Code?
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:
ifRather 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.
- SignUpForm exampleFor components requiring various variations, explicit expression is better than abstraction.
Name complex conditions
isEmpty,shouldShowTooltip,isUnauthorizedUserNaming them like this significantly increases code readability.- Conditional rendering also,
if (조건) return null→if (shouldSkipRender)changing it to something like this makes it readable at a glance.
Eliminate Magic Numbers
300,24 * 60 * 60 * 1000Extract values like this as constants to reveal their meaning.TIME_MS.DEBOUNCE.SEARCH,FIVE_MINUTESUsing it like this makes it much easier to modify later.
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,httpLibraryDistinguish them by revealing their roles, like
Return the same hook with the same structure
useQueryIf the team establishes a rule that hooks created with `useQuery` return a query object, it becomes easier to predict.
Structure validation using Discriminated Unions
// 타입
{ ok: true } | { ok: false; reason: string }
- Doing this automatically narrows down types during conditional branching and prevents errors.
- Refer to Basarat's article
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.tsxshould 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).
