Prefer ObsoletedUser over NewUser for breaking changes Nov 13, 2021

When updating a variable or type, it is a better practice to rename the old one to ObsoletedX and keep the active one as X. This approach enhances clarity, simplifies transitions and refactoring, minimizes errors, and improves overall code readability and maintainability. Adopting this naming convention is a small but powerful step towards writing cleaner, more maintainable code.

In the ever-evolving landscape of software development, updates and refactoring are routine. However, the manner in which these updates are implemented can significantly impact code maintainability and readability. A common scenario developers face is updating a variable or type, such as transitioning from an old user model to a new one. The intuitive approach might be to introduce a NewUser type while keeping the old one as User. However, a more effective strategy is to rename the old type to ObsoletedUser and retain the active one as User.

1. Clarity and intention

Renaming the old type to ObsoletedUser and keeping the new one as User immediately communicates the state and purpose of each type. It clarifies that ObsoletedUser is deprecated and should not be used in new code, while User is the current, active type. This naming convention reduces ambiguity and helps developers understand the codebase more quickly.

2. Ease of transition

When a new developer joins the team or when someone revisits the code after a long period, having a clear distinction between deprecated and active types simplifies the onboarding process. They can easily identify which type to use without second-guessing or digging through documentation. This practice minimizes the cognitive overhead associated with understanding the codebase.

3. Refactoring simplicity

Refactoring code to replace an old type with a new one can be a daunting task, especially in large codebases. By keeping the active type as User and marking the old one as ObsoletedUser, you streamline the refactoring process. Developers can methodically replace instances of ObsoletedUser without worrying about inadvertently affecting the new type.

4. Minimizing errors

Introducing a NewUser type while keeping the old one as User can lead to confusion and potential errors. Developers might mistakenly use User when they intend to use NewUser or vice versa. This naming convention can lead to bugs and inconsistencies in the code. By clearly delineating the deprecated type, you reduce the risk of such errors.

5. Code readability and maintenance

Good code is not just about functionality; it’s also about readability and maintainability. Clear and intentional naming conventions contribute significantly to these aspects. When the active type remains as User, and the deprecated type is renamed to ObsoletedUser, the codebase remains clean and intuitive. Future maintenance becomes more manageable, and the overall quality of the code improves.

Let’s consider a simple TypeScript example.

interface User {
  id: number;
  name: string;
  email: string;
}

function getUserInfo(user: User): string {
  return User: ${user.name}, Email: ${user.email};
}

const user: User = { id: 1, name: "Alice", email: "alice@example.com" };
console.log(getUserInfo(user));

Instead of introducing a NewUser type, we rename the old type to ObsoletedUser and keep the active one as User.

interface ObsoletedUser {
  id: number;
  name: string;
  email: string;
}

interface User {
  id: number;
  fullName: string;
  emailAddress: string;
}

function getUserInfo(user: User): string {
  return User: ${user.fullName}, Email: ${user.emailAddress};
}

const newUser: User = { id: 1, fullName: "Alice Johnson", emailAddress: "alice.johnson@example.com" };
console.log(getUserInfo(newUser));

function getObsoletedUserInfo(user: ObsoletedUser): string {
  return User: ${user.name}, Email: ${user.email};
}

const oldUser: ObsoletedUser = { id: 2, name: "Bob", email: "bob@example.com" };
console.log(getObsoletedUserInfo(oldUser));