Mid-Sized Components and Functions: Best Practices for Modular and Maintainable Code
I've seen two extremes. Functions that are 500 lines long, doing everything from validation to database writes to email sending. And functions that are 3 ...
16 Apr 2024

I've seen two extremes. Functions that are 500 lines long, doing everything from validation to database writes to email sending. And functions that are 3 lines long, each calling another 3-line function, creating a stack trace ten levels deep where nothing substantial happens anywhere.
Both are hard to work with. The sweet spot is in the middle.
What mid-sized means
A mid-sized function or component is roughly 50 to 200 lines. It's big enough to contain a meaningful chunk of logic. Small enough to fit in your head.
It handles one feature, one workflow, or one concern. Not atomic enough for a single operation. Not large enough to be its own module or service.
Examples: a checkout flow processor, a form validation pipeline, a data transformation for a specific report.
Why mid-sized is the sweet spot
You can read it. A 100-line function can be understood in a few minutes. A 500-line function takes an afternoon.
You can test it. A focused function with clear inputs and outputs is easy to write tests for. A monolith with 12 dependencies requires a test setup that's harder than the code itself.
You can change it. When business requirements shift, modifying a mid-sized function is a contained task. Modifying a god function means understanding the entire thing before touching any part of it.
You can reuse it. Functions that do one thing well get reused. Functions that do five things don't, because callers only need two of the five.
Guidelines for sizing
If you need to scroll, it's probably too long. Not a hard rule, but a useful signal. If you can't see the whole function on one screen, consider splitting it.
If the function name needs "and," it's doing too much. validateAndSaveAndNotify is three functions pretending to be one.
If you can name a section with a comment, extract it. A comment like // calculate shipping costs inside a larger function is a strong signal that block should be its own function called calculateShippingCosts.
If you can't name it, don't extract it. Not every block of code deserves to be a function. If you can't find a clear, honest name, the extraction might create more confusion than it solves.
Components follow the same rules
In React, Angular, or any component framework, the same principle applies. A component that handles layout, state management, API calls, and error display is too big. A component that renders a single <span> is too small (unless it truly encapsulates meaningful behavior).
Aim for components that own one visual concern or one interaction flow.
The trade-off
Smaller functions mean more files to navigate and more names to invent. There's a real cost to splitting too aggressively: death by a thousand files where no single file contains enough context to be useful.
The balance: extract when a function does multiple distinct things. Keep it together when the logic is linear and cohesive. Trust your judgment. If a teammate needs to bounce between six files to understand one workflow, you've split too far.
Keep reading
- DI, SOLID, and the Fundamentals That Actually Matter
- Coding Principles for Better Code Quality
- Right Balance: Best Practices for Code Comments in TypeScript
- Best Practices for Writing Isolated Tests in TypeScript
- Meaningful Test Concepts: Best Practices for Writing Tests in TypeScript
- Open-Closed Principle (OCP) Best Practices in TypeScript: Guidelines and Examples