SpecThis logo

File Line Count

Managing file size for better code organization

What is File Line Count?

File line count measures the total number of lines in a source file, including code, comments, blank lines, and import statements. While not as precise as logical LOC, it provides a quick indicator of file size and complexity.

Large files are harder to navigate, understand, and maintain. They often indicate poor separation of concerns and can slow down development tools and code reviews.

How It's Calculated

Simple Count

The metric counts every line in the file from the first line to the last line, including:

  • Code statements
  • Comments and JSDoc
  • Blank lines (for readability)
  • Import/export statements
  • Type definitions
  • Opening and closing braces

Examples

Example 1: Small File (45 lines)

A focused utility file with a single purpose.

// utils/date-formatter.ts

/**
 * Date formatting utilities
 */

import { format, parseISO } from "date-fns";

/**
 * Format a date string to a readable format
 */
export function formatDate(dateString: string): string {
  try {
    const date = parseISO(dateString);
    return format(date, "MMM d, yyyy");
  } catch {
    return "Invalid date";
  }
}

/**
 * Format a date with time
 */
export function formatDateTime(dateString: string): string {
  try {
    const date = parseISO(dateString);
    return format(date, "MMM d, yyyy 'at' h:mm a");
  } catch {
    return "Invalid date";
  }
}

/**
 * Get relative time (e.g., "2 hours ago")
 */
export function getRelativeTime(dateString: string): string {
  // Implementation
  return "...";
}

// Total: ~45 lines
// Well-organized, single purpose, easy to find

Example 2: Large File (1200+ lines) - Problematic

A "god file" that tries to do everything related to users.

// user-service.ts

// 50 lines of imports
import { db } from "./db";
import { email } from "./email";
import { auth } from "./auth";
// ... 40+ more imports

// 100 lines of types
interface User { /* ... */ }
interface UserProfile { /* ... */ }
interface UserSettings { /* ... */ }
// ... 20+ more types

// 150 lines of validation
function validateEmail(email: string) { /* ... */ }
function validatePassword(password: string) { /* ... */ }
// ... 30+ validation functions

// 200 lines of user CRUD
export async function createUser() { /* ... */ }
export async function getUser() { /* ... */ }
export async function updateUser() { /* ... */ }
export async function deleteUser() { /* ... */ }
// ... 15+ CRUD functions

// 200 lines of authentication
export async function login() { /* ... */ }
export async function logout() { /* ... */ }
export async function resetPassword() { /* ... */ }
// ... 20+ auth functions

// 150 lines of profile management
export async function updateProfile() { /* ... */ }
export async function uploadAvatar() { /* ... */ }
// ... 15+ profile functions

// 200 lines of user settings
export async function getSettings() { /* ... */ }
export async function updateSettings() { /* ... */ }
// ... 20+ settings functions

// 100 lines of analytics
export async function trackLogin() { /* ... */ }
export async function getUserStats() { /* ... */ }
// ... 10+ analytics functions

// 50 lines of helpers
function hashPassword() { /* ... */ }
function generateToken() { /* ... */ }
// ... 10+ helpers

// Total: 1200+ lines
// Impossible to navigate, multiple concerns, hard to maintain

Example 3: Refactored Structure (100-300 lines each)

Breaking the large file into focused modules.

// users/
//   types/
//     user.ts                    (~50 lines - type definitions)
//     user-profile.ts            (~40 lines)
//     user-settings.ts           (~40 lines)
//
//   validation/
//     email-validator.ts         (~30 lines)
//     password-validator.ts      (~50 lines)
//     user-validator.ts          (~80 lines)
//
//   repositories/
//     user-repository.ts         (~200 lines - CRUD operations)
//
//   services/
//     auth-service.ts            (~250 lines - authentication)
//     profile-service.ts         (~150 lines - profile management)
//     settings-service.ts        (~180 lines - user settings)
//     analytics-service.ts       (~120 lines - tracking)
//
//   utils/
//     password-utils.ts          (~60 lines)
//     token-utils.ts             (~70 lines)
//
//   index.ts                     (~30 lines - public exports)

// Each file is focused and manageable!

Problems with Large Files

Developer Experience

  • Hard to find specific functions
  • Slow IDE performance
  • Difficult to navigate
  • Overwhelming to new developers
  • Hard to understand scope

Code Quality

  • Poor separation of concerns
  • High coupling
  • Merge conflicts more likely
  • Harder to test
  • Difficult code reviews

Recommended Thresholds

Sensitivity LevelMax LinesUse Case
High≤ 300Well-organized codebases
Medium≤ 500Most applications
Low≤ 800Legacy code being improved

Refactoring Strategies

1. Split by Domain

Separate code by business domain or feature.

Example:

user-service.ts
user-auth.ts,user-profile.ts,user-settings.ts

2. Extract Types

Move type definitions to separate types.ts files. This is especially helpful in TypeScript projects.

3. Extract Constants

Move configuration and constants to separate constants.ts orconfig.ts files.

4. Use Index Files

Create index.ts files to re-export from a directory, keeping the public API clean.

// users/index.ts
export * from "./user-auth";
export * from "./user-profile";
export * from "./user-settings";
export type { User, UserProfile } from "./types";

// Consumers import from the directory
import { login, updateProfile } from "./users";

5. Follow the Single Responsibility Principle

Each file should have one clear purpose. If you struggle to name a file concisely, it's probably doing too much.

Best Practices

File Organization Guidelines

  • One export per file: Easier to find and import specific functionality
  • Group by feature: Related files should be close together
  • Consistent structure: Follow the same patterns across your codebase
  • Meaningful names: File names should clearly indicate their purpose
  • Avoid "utils": Be more specific than generic utility files

Related Metrics

  • Logical LOC: High logical LOC per function often means high file line count
  • Number of Functions: Files with many functions are usually large
  • Number of Imports: Many imports can indicate file is doing too much