In the world of software development, we often talk about writing "clean code," but what does that really mean in practice? For me, it means creating a codebase that feels like a well-organized library - where each component, hook, and utility has a clear purpose, and the overall structure makes intuitive sense. It's about building software that not only works today but remains a joy to work with months and years down the line.
Early in my development journey, I found myself writing components that did everything: managing state, handling API calls, processing data, and rendering UI. Take this hypothetical example:
// ❌ The "everything component" anti-pattern
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
const userData = await response.json();
setUser(userData);
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
const updateUser = async (updates) => {
// Complex update logic here...
};
const formatUserName = (user) => {
// String formatting logic here...
};
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>{formatUserName(user)}</h1>
{/* More complex UI here */}
</div>
);
}
This component is doing too much - it's managing state, making API calls, formatting data, and rendering UI. It's hard to test, hard to reuse, and hard to maintain.
Instead, I believe in a clean separation where components focus purely on UI rendering, while business logic lives in dedicated hooks and utilities. Here's how I approach it:
Components should be like well-designed UI templates - they receive data and render it beautifully, but they don't concern themselves with where that data comes from or how it's processed.
// ✅ Clean component focused on UI
function UserProfile({ user, loading, error, onUpdate }) {
if (loading) return <div>Loading...</div>;
if (error) return <div>Error: {error}</div>;
return (
<div>
<h1>{user.name}</h1>
<button onClick={() => onUpdate({ name: "New Name" })}>
Update Name
</button>
</div>
);
}
Hooks handle all the complex logic - API calls, state management, side effects. They're reusable and testable.
// ✅ Business logic abstracted to a hook
function useUser(userId) {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchUser = async () => {
setLoading(true);
try {
const response = await fetch(`/api/users/${userId}`);
setUser(await response.json());
} catch (err) {
setError(err.message);
} finally {
setLoading(false);
}
};
fetchUser();
}, [userId]);
const updateUser = async (updates) => {
// Update logic here...
};
return { user, loading, error, updateUser };
}
Pure functions handle data transformations, calculations, and formatting - anything that doesn't involve side effects.
// ✅ Pure utility functions
export function formatUserName(user) {
if (!user) return "";
return `${user.firstName} ${user.lastName}`.trim();
}
export function calculateUserAge(birthDate) {
// Age calculation logic...
}
Let me show you how this philosophy plays out in my actual blog project. Take the WindowFrame component - it creates the beautiful wooden frame around my blog content, but it used to be a mess of mixed concerns.
// ❌ Old WindowFrame - logic mixed with UI
export function WindowFrame({ children }) {
const [clickCount, setClickCount] = useState(0);
const [lastClickTime, setLastClickTime] = useState(0);
const router = useRouter();
const handleTopFrameClick = () => {
// 15 lines of click tracking logic...
};
const getThemeClasses = () => {
// 20 lines of theme calculation logic...
};
// More mixed logic...
}
// ✅ New WindowFrame - focused on UI
export function WindowFrame({ children }) {
const { handleClick } = useAdminAccess();
const animatedElements = useAnimatedElements(backgroundElements);
const themeClasses = getThemeClasses(theme);
return (
<div className={`${themeClasses} ${getSeasonOverlay(season)}`}>
{/* Clean, focused UI rendering */}
</div>
);
}
The business logic moved to dedicated places:
useAdminAccess hookuseAnimatedElements hookgetThemeClasses utilityWhen I need to change how admin access works, I only touch the useAdminAccess hook. The component stays clean and focused.
I can unit test the admin access logic independently of the UI. No need to render components to test business logic.
The useAdminAccess hook could be used in any component that needs admin functionality. The getThemeClasses utility works across the entire app.
New developers can quickly understand what each piece does. Components are self-documenting through their clean interfaces.
As the project grows, the architecture naturally accommodates new features without becoming unwieldy.
I build software with the understanding that code is read far more often than it's written. By keeping components high-level and abstracting logic appropriately, I create a codebase that tells a clear story - not just about what the code does, but why it does it that way.
This approach isn't about following trends or using the latest frameworks. It's about creating software that stands the test of time, that can be easily modified, extended, and maintained by future developers (including future me).
In the end, clean architecture isn't just about writing better code - it's about creating a better development experience and building software that truly serves its users over the long term. 🎯